koffi 2.5.21-beta.4 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- 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 +2 -16
- package/doc/functions.md +16 -0
- package/doc/index.rst +1 -0
- package/doc/polymorphism.md +1 -1
- package/doc/variables.md +100 -0
- package/package.json +2 -2
- package/src/index.js +2 -2
- package/src/koffi/src/parser.cc +4 -14
- package/src/koffi/src/parser.hh +1 -1
- package/src/koffi/src/util.cc +44 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
## Version history
|
|
4
4
|
|
|
5
|
+
### Koffi 2.6
|
|
6
|
+
|
|
7
|
+
#### Koffi 2.6.1 (2023-09-18)
|
|
8
|
+
|
|
9
|
+
- Support string direction qualifiers in classic function definitions
|
|
10
|
+
- Fix possible off-by-one array access when parsing type name
|
|
11
|
+
|
|
12
|
+
#### Koffi 2.6.0 (2023-09-13)
|
|
13
|
+
|
|
14
|
+
**New features:**
|
|
15
|
+
|
|
16
|
+
- Use [koffi.symbol()](variables.md#variable-definitions) to make pointers to exported variables (or other symbols)
|
|
17
|
+
- Use [koffi.encode()](variables.md#encode-to-c-memory) to explictly encode data from JS to C memory
|
|
18
|
+
- Use shared library [lazy-loading](functions.md#loading-options) (RTLD_LAZY) on POSIX platforms
|
|
19
|
+
|
|
20
|
+
**Other changes:**
|
|
21
|
+
|
|
22
|
+
- Print more helpful error for Win32/Win64 DLL mismatches
|
|
23
|
+
- Add missing 'Union' primitive value in TS definition file
|
|
24
|
+
|
|
5
25
|
### Koffi 2.5
|
|
6
26
|
|
|
7
27
|
#### Koffi 2.5.20 (2023-08-31)
|
|
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
|
|
Binary file
|
package/doc/callbacks.md
CHANGED
|
@@ -153,14 +153,9 @@ let cb2 = koffi.register(store, store.get, 'IntCallback *'); // However in this
|
|
|
153
153
|
|
|
154
154
|
Koffi does not have enough information to convert callback pointer arguments to an appropriate JS value. In this case, your JS function will receive an opaque *External* object.
|
|
155
155
|
|
|
156
|
-
You can pass this value through to another C function that expects a pointer of the same type, or you can use
|
|
156
|
+
You can pass this value through to another C function that expects a pointer of the same type, or you can use [koffi.decode()](variables.md#decode-to-js-values) function to decode pointer arguments.
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
- `koffi.decode(value, type)`: no offset, expect NUL-terminated strings
|
|
161
|
-
- `koffi.decode(value, offset, type)`: explicit offset to add to the pointer before decoding
|
|
162
|
-
|
|
163
|
-
The following example sorts an array of strings (in-place) with `qsort()`:
|
|
158
|
+
The following examples uses it to sort an array of strings in-place with the standard C function `qsort()`:
|
|
164
159
|
|
|
165
160
|
```js
|
|
166
161
|
// ES6 syntax: import koffi from 'koffi';
|
|
@@ -183,15 +178,6 @@ qsort(koffi.as(array, 'char **'), array.length, koffi.sizeof('void *'), (ptr1, p
|
|
|
183
178
|
console.log(array); // Prints ['123', 'bar', 'foo', 'foobar']
|
|
184
179
|
```
|
|
185
180
|
|
|
186
|
-
There is also an optional ending `length` argument that you can use in two cases:
|
|
187
|
-
|
|
188
|
-
- Use it to give the number of bytes to decode in non-NUL terminated strings: `koffi.decode(value, 'char *', 5)`
|
|
189
|
-
- Decode consecutive values into an array. For example, here is how you can decode an array with 3 float values: `koffi.decode(value, 'float', 3)`. This is equivalent to `koffi.decode(value, koffi.array('float', 3))`.
|
|
190
|
-
|
|
191
|
-
```{note}
|
|
192
|
-
In Koffi 2.2 and earlier versions, the length argument is only used to decode strings and is ignored otherwise.
|
|
193
|
-
```
|
|
194
|
-
|
|
195
181
|
### Asynchronous callbacks
|
|
196
182
|
|
|
197
183
|
*New in Koffi 2.2.2*
|
package/doc/functions.md
CHANGED
|
@@ -19,6 +19,22 @@ Starting with *Koffi 2.3.20*, you can explicitly unload a library by calling `li
|
|
|
19
19
|
On some platforms (such as with the [musl C library on Linux](https://wiki.musl-libc.org/functional-differences-from-glibc.html#Unloading-libraries)), shared libraries cannot be unloaded, so the library will remain loaded and memory mapped after the call to `lib.unload()`.
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
## Loading options
|
|
23
|
+
|
|
24
|
+
*New in Koffi 2.6*
|
|
25
|
+
|
|
26
|
+
The `load` function can take an optional object argument, with the following options:
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
const options = {
|
|
30
|
+
lazy: true // Use RTLD_LAZY (lazy-binding) on POSIX platforms (by default, use RTLD_NOW)
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const lib = koffi.load('/path/to/shared/library.so', options);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
More options may be added if needed.
|
|
37
|
+
|
|
22
38
|
## Function definitions
|
|
23
39
|
|
|
24
40
|
### Definition syntax
|
package/doc/index.rst
CHANGED
package/doc/polymorphism.md
CHANGED
|
@@ -105,4 +105,4 @@ console.log(vec1); // { x: 3, y: 2, z: 1 }
|
|
|
105
105
|
console.log(vec2); // { x: 1, y: 2, z: 3 }
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
See [
|
|
108
|
+
See [decoding variables](variables.md#decode-to-js-values) for more information about the decode function.
|
package/doc/variables.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Exported variables
|
|
2
|
+
|
|
3
|
+
## Variable definitions
|
|
4
|
+
|
|
5
|
+
*New in Koffi 2.6*
|
|
6
|
+
|
|
7
|
+
To find an exported and declare a variable, use `lib.symbol(name, type)`. You need to specify its name and its type.
|
|
8
|
+
|
|
9
|
+
```c
|
|
10
|
+
int my_int = 42;
|
|
11
|
+
const char *my_string = NULL;
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
const my_int = lib.symbol('my_int', 'int');
|
|
16
|
+
const my_string = lib.symbol('my_string', 'const char *');
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
You cannot directly manipulate these variables, use:
|
|
20
|
+
|
|
21
|
+
- [koffi.decode()](#decode-to-js-values) to read their value
|
|
22
|
+
- [koffi.encode()](#encode-to-c-memory) to change their valuèe
|
|
23
|
+
|
|
24
|
+
## Decode to JS values
|
|
25
|
+
|
|
26
|
+
*New in Koffi 2.2, changed in Koffi 2.3*
|
|
27
|
+
|
|
28
|
+
Use `koffi.decode()` to decode C pointers, wrapped as external objects or as simple numbers.
|
|
29
|
+
|
|
30
|
+
Some arguments are optional and this function can be called in several ways:
|
|
31
|
+
|
|
32
|
+
- `koffi.decode(value, type)`: no offset
|
|
33
|
+
- `koffi.decode(value, offset, type)`: explicit offset to add to the pointer before decoding
|
|
34
|
+
|
|
35
|
+
By default, Koffi expects NUL terminated strings when decoding them. See below if you need to specify the string length.
|
|
36
|
+
|
|
37
|
+
The following example illustrates how to decode an integer and a C string variable.
|
|
38
|
+
|
|
39
|
+
```c
|
|
40
|
+
int my_int = 42;
|
|
41
|
+
const char *my_string = "foobar";
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const my_int = lib.symbol('my_int', 'int');
|
|
46
|
+
const my_string = lib.symbol('my_string', 'const char *');
|
|
47
|
+
|
|
48
|
+
console.log(koffi.decode(my_int, 'int')) // Prints 42
|
|
49
|
+
console.log(koffi.decode(my_string, 'const char *')) // Prints "foobar"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
There is also an optional ending `length` argument that you can use in two cases:
|
|
53
|
+
|
|
54
|
+
- Use it to give the number of bytes to decode in non-NUL terminated strings: `koffi.decode(value, 'char *', 5)`
|
|
55
|
+
- Decode consecutive values into an array. For example, here is how you can decode an array with 3 float values: `koffi.decode(value, 'float', 3)`. This is equivalent to `koffi.decode(value, koffi.array('float', 3))`.
|
|
56
|
+
|
|
57
|
+
Thge example below will decode the symbol `my_string` defined above but only the first three bytes.
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
// Only decode 3 bytes from the C string my_string
|
|
61
|
+
console.log(koffi.decode(my_string, 'const char *', 3)) // Prints "foo"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```{note}
|
|
65
|
+
In Koffi 2.2 and earlier versions, the length argument is only used to decode strings and is ignored otherwise.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Encode to C memory
|
|
69
|
+
|
|
70
|
+
*New in Koffi 2.6*
|
|
71
|
+
|
|
72
|
+
Use `koffi.encode()` to encode C pointers, wrapped as external objects or as simple numbers.
|
|
73
|
+
|
|
74
|
+
Some arguments are optional and this function can be called in several ways:
|
|
75
|
+
|
|
76
|
+
- `koffi.encode(ref, type, value)`: no offset
|
|
77
|
+
- `koffi.encode(ref, offset, type, value)`: explicit offset to add to the pointer before encoding
|
|
78
|
+
|
|
79
|
+
We'll reuse the examples shown above and change the variable values with `koffi.encode()`.
|
|
80
|
+
|
|
81
|
+
```c
|
|
82
|
+
int my_int = 42;
|
|
83
|
+
const char *my_string = NULL;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
const my_int = lib.symbol('my_int', 'int');
|
|
88
|
+
const my_string = lib.symbol('my_string', 'const char *');
|
|
89
|
+
|
|
90
|
+
console.log(koffi.decode(my_int, 'int')) // Prints 42
|
|
91
|
+
console.log(koffi.decode(my_string, 'const char *')) // Prints null
|
|
92
|
+
|
|
93
|
+
koffi.encode(my_int, 'int', -1);
|
|
94
|
+
koffi.encode(my_string, 'const char *', 'Hello World!');
|
|
95
|
+
|
|
96
|
+
console.log(koffi.decode(my_int, 'int')) // Prints -1
|
|
97
|
+
console.log(koffi.decode(my_string, 'const char *')) // Prints "Hello World!"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
When encoding strings (either directly or embedded in arrays or structs), the memory will be bound to the raw pointer value and managed by Koffi. You can assign to the same string again and again without any leak or risk of use-after-free.
|
package/package.json
CHANGED
package/src/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.
|
|
382
|
-
stable: "2.
|
|
381
|
+
version: "2.6.1",
|
|
382
|
+
stable: "2.6.1",
|
|
383
383
|
description: "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
384
384
|
keywords: [
|
|
385
385
|
"foreign",
|
package/src/koffi/src/parser.cc
CHANGED
|
@@ -35,7 +35,7 @@ bool PrototypeParser::Parse(const char *str, FunctionInfo *out_func)
|
|
|
35
35
|
|
|
36
36
|
Tokenize(str);
|
|
37
37
|
|
|
38
|
-
out_func->ret.type = ParseType();
|
|
38
|
+
out_func->ret.type = ParseType(nullptr);
|
|
39
39
|
if (!CanReturnType(out_func->ret.type)) {
|
|
40
40
|
MarkError("You are not allowed to directly return %1 values (maybe try %1 *)", out_func->ret.type->name);
|
|
41
41
|
return false;
|
|
@@ -62,17 +62,7 @@ bool PrototypeParser::Parse(const char *str, FunctionInfo *out_func)
|
|
|
62
62
|
break;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
param.directions = 1;
|
|
67
|
-
} else if (Match("_Out_")) {
|
|
68
|
-
param.directions = 2;
|
|
69
|
-
} else if (Match("_Inout_")) {
|
|
70
|
-
param.directions = 3;
|
|
71
|
-
} else {
|
|
72
|
-
param.directions = 1;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
param.type = ParseType();
|
|
65
|
+
param.type = ParseType(¶m.directions);
|
|
76
66
|
|
|
77
67
|
if (!CanPassType(param.type, param.directions)) {
|
|
78
68
|
MarkError("Type %1 cannot be used as a parameter", param.type->name);
|
|
@@ -145,7 +135,7 @@ void PrototypeParser::Tokenize(const char *str)
|
|
|
145
135
|
}
|
|
146
136
|
}
|
|
147
137
|
|
|
148
|
-
const TypeInfo *PrototypeParser::ParseType()
|
|
138
|
+
const TypeInfo *PrototypeParser::ParseType(int *out_directions)
|
|
149
139
|
{
|
|
150
140
|
Size start = offset;
|
|
151
141
|
|
|
@@ -166,7 +156,7 @@ const TypeInfo *PrototypeParser::ParseType()
|
|
|
166
156
|
|
|
167
157
|
while (offset >= start) {
|
|
168
158
|
Span<const char> str = MakeSpan(tokens[start].ptr, tokens[offset].end() - tokens[start].ptr);
|
|
169
|
-
const TypeInfo *type = ResolveType(env, str);
|
|
159
|
+
const TypeInfo *type = ResolveType(env, str, out_directions);
|
|
170
160
|
|
|
171
161
|
if (type) {
|
|
172
162
|
offset++;
|
package/src/koffi/src/parser.hh
CHANGED
package/src/koffi/src/util.cc
CHANGED
|
@@ -153,6 +153,22 @@ static inline bool IsIdentifierChar(char c)
|
|
|
153
153
|
return IsAsciiAlphaOrDigit(c) || c == '_';
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
static inline Span<const char> SplitIdentifier(Span<const char> str)
|
|
157
|
+
{
|
|
158
|
+
Size offset = 0;
|
|
159
|
+
|
|
160
|
+
if (str.len && IsIdentifierStart(str[0])) {
|
|
161
|
+
offset++;
|
|
162
|
+
|
|
163
|
+
while (offset < str.len && IsIdentifierChar(str[offset])) {
|
|
164
|
+
offset++;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
Span<const char> token = str.Take(0, offset);
|
|
169
|
+
return token;
|
|
170
|
+
}
|
|
171
|
+
|
|
156
172
|
const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_directions)
|
|
157
173
|
{
|
|
158
174
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
@@ -160,8 +176,31 @@ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_direct
|
|
|
160
176
|
int indirect = 0;
|
|
161
177
|
uint8_t disposables = 0;
|
|
162
178
|
|
|
179
|
+
// Consume parameter direction qualifier
|
|
180
|
+
if (out_directions) {
|
|
181
|
+
if (str.len && str[0] == '_') {
|
|
182
|
+
Span<const char> qualifier = SplitIdentifier(str);
|
|
183
|
+
|
|
184
|
+
if (qualifier == "_In_") {
|
|
185
|
+
*out_directions = 1;
|
|
186
|
+
str = str.Take(5, str.len - 5);
|
|
187
|
+
} else if (qualifier == "_Out_") {
|
|
188
|
+
*out_directions = 2;
|
|
189
|
+
str = str.Take(6, str.len - 6);
|
|
190
|
+
} else if (qualifier == "_Inout_") {
|
|
191
|
+
*out_directions = 3;
|
|
192
|
+
str = str.Take(8, str.len - 8);
|
|
193
|
+
} else {
|
|
194
|
+
*out_directions = 1;
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
*out_directions = 1;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
163
201
|
// Skip initial const qualifiers
|
|
164
|
-
|
|
202
|
+
str = TrimStr(str);
|
|
203
|
+
while (SplitIdentifier(str) == "const") {
|
|
165
204
|
str = str.Take(6, str.len - 6);
|
|
166
205
|
str = TrimStr(str);
|
|
167
206
|
}
|
|
@@ -173,13 +212,10 @@ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_direct
|
|
|
173
212
|
for (;;) {
|
|
174
213
|
remain = TrimStr(remain);
|
|
175
214
|
|
|
176
|
-
|
|
215
|
+
Span<const char> token = SplitIdentifier(remain);
|
|
216
|
+
if (!token.len)
|
|
177
217
|
break;
|
|
178
|
-
|
|
179
|
-
do {
|
|
180
|
-
remain.ptr++;
|
|
181
|
-
remain.len--;
|
|
182
|
-
} while (remain.len && IsIdentifierChar(remain[0]));
|
|
218
|
+
remain = remain.Take(token.len, remain.len - token.len);
|
|
183
219
|
}
|
|
184
220
|
|
|
185
221
|
Span<const char> name = TrimStr(MakeSpan(str.ptr, remain.ptr - str.ptr));
|
|
@@ -197,7 +233,7 @@ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_direct
|
|
|
197
233
|
} else if (remain[0] == '!') {
|
|
198
234
|
remain = remain.Take(1, remain.len - 1);
|
|
199
235
|
disposables |= (1u << indirect);
|
|
200
|
-
} else if (remain
|
|
236
|
+
} else if (SplitIdentifier(remain) == "const") {
|
|
201
237
|
remain = remain.Take(6, remain.len - 6);
|
|
202
238
|
} else {
|
|
203
239
|
break;
|
|
@@ -263,9 +299,6 @@ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_direct
|
|
|
263
299
|
RG_ASSERT(type);
|
|
264
300
|
}
|
|
265
301
|
|
|
266
|
-
if (out_directions) {
|
|
267
|
-
*out_directions = 1;
|
|
268
|
-
}
|
|
269
302
|
return type;
|
|
270
303
|
}
|
|
271
304
|
|