koffi 2.7.4-beta.1 → 2.8.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 +17 -3
- 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/functions.md +0 -1
- package/doc/index.rst +0 -1
- package/doc/input.md +1 -1
- package/doc/output.md +108 -0
- package/doc/pointers.md +64 -1
- package/index.js +2 -2
- package/indirect.js +2 -2
- package/package.json +2 -2
- package/src/cnoke/assets/FindCNoke.cmake +8 -4
- package/src/koffi/CMakeLists.txt +3 -4
- package/src/koffi/src/call.cc +5 -5
- package/src/koffi/src/ffi.cc +186 -114
- package/src/koffi/src/ffi.hh +3 -0
- package/doc/polymorphism.md +0 -108
package/CHANGELOG.md
CHANGED
|
@@ -2,8 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## Version history
|
|
4
4
|
|
|
5
|
+
### Koffi 2.8
|
|
6
|
+
|
|
7
|
+
#### Koffi 2.8.0 (2024-02-12)
|
|
8
|
+
|
|
9
|
+
- Support pushing pointers for string arguments
|
|
10
|
+
- Add `koffi.alloc()` for [stable pointers](output.md#stable-pointers)
|
|
11
|
+
|
|
5
12
|
### Koffi 2.7
|
|
6
13
|
|
|
14
|
+
#### Koffi 2.7.4 (2024-02-04)
|
|
15
|
+
|
|
16
|
+
- Support callbacks happening on main thread but outside JS call (such as during event loop)
|
|
17
|
+
- Improve stability after `koffi.reset()`
|
|
18
|
+
- Expose internal Node/NAPI `env` pointer
|
|
19
|
+
- Name main Koffi API functions
|
|
20
|
+
|
|
7
21
|
#### Koffi 2.7.3 (2024-01-26)
|
|
8
22
|
|
|
9
23
|
- Support decoding NULL terminated arrays
|
|
@@ -123,7 +137,7 @@ Pre-built binaries don't work correctly in Koffi 2.5.13 to 2.5.15, skip those ve
|
|
|
123
137
|
|
|
124
138
|
#### Koffi 2.5.11 (2023-08-03)
|
|
125
139
|
|
|
126
|
-
- Support casting function pointers with [koffi.as()](
|
|
140
|
+
- Support casting function pointers with [koffi.as()](pointers.md#handling-void-pointers)
|
|
127
141
|
- Build in C++20 mode
|
|
128
142
|
|
|
129
143
|
#### Koffi 2.5.10 (2023-08-01)
|
|
@@ -331,7 +345,7 @@ Pre-built binaries don't work correctly in Koffi 2.5.13 to 2.5.15, skip those ve
|
|
|
331
345
|
**Main changes:**
|
|
332
346
|
|
|
333
347
|
- Allow buffers (TypedArray or ArrayBuffer) values for input and/or output pointer arguments (for polymorphic arguments)
|
|
334
|
-
- Support opaque buffers (TypedArray or ArrayBuffer) values in `koffi.decode()` to [decode output buffers](
|
|
348
|
+
- Support opaque buffers (TypedArray or ArrayBuffer) values in `koffi.decode()` to [decode output buffers](output.md#output-buffers)
|
|
335
349
|
- Decode non-string types as arrays when an [explicit length is passed to koffi.decode()](callbacks.md#decoding-pointer-arguments)
|
|
336
350
|
|
|
337
351
|
**Other changes:**
|
|
@@ -424,7 +438,7 @@ Pre-built binaries don't work correctly in Koffi 2.5.13 to 2.5.15, skip those ve
|
|
|
424
438
|
|
|
425
439
|
**Main changes:**
|
|
426
440
|
|
|
427
|
-
- Add [koffi.as()](
|
|
441
|
+
- Add [koffi.as()](pointers.md#handling-void-pointers) to support polymorphic APIs based on `void *` parameters
|
|
428
442
|
- Add [endian-sensitive integer types](input.md#endian-sensitive-integers): `intX_le_t`, `intX_be_t`, `uintX_le_t`, `uintX_be_t`
|
|
429
443
|
- Accept typed arrays for `void *` parameters
|
|
430
444
|
- Introduce `koffi.opaque()` to replace `koffi.handle()` (which remains supported until Koffi 3.0)
|
|
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/functions.md
CHANGED
|
@@ -251,4 +251,3 @@ Among other thing, in the the following pages you will learn more about:
|
|
|
251
251
|
- How Koffi translates [input parameters](input.md) to C
|
|
252
252
|
- How you can [define and use pointers](pointers.md)
|
|
253
253
|
- How to deal with [output parameters](output.md)
|
|
254
|
-
- How to handle [polymorphic API](polymorphism.md)
|
package/doc/index.rst
CHANGED
package/doc/input.md
CHANGED
|
@@ -393,7 +393,7 @@ The reverse case is also true, Koffi can convert a C fixed-size buffer to a JS s
|
|
|
393
393
|
|
|
394
394
|
### Dynamic arrays (pointers)
|
|
395
395
|
|
|
396
|
-
In C, dynamically-sized arrays are usually passed around as pointers. Read more about [array pointers](pointers.md#
|
|
396
|
+
In C, dynamically-sized arrays are usually passed around as pointers. Read more about [array pointers](pointers.md#dynamic-arrays) in the relevant section.
|
|
397
397
|
|
|
398
398
|
## Union types
|
|
399
399
|
|
package/doc/output.md
CHANGED
|
@@ -167,3 +167,111 @@ ConcatToBuffer(str1, str2, out);
|
|
|
167
167
|
|
|
168
168
|
console.log(out[0]);
|
|
169
169
|
```
|
|
170
|
+
|
|
171
|
+
## Output buffers
|
|
172
|
+
|
|
173
|
+
In most cases, you can use buffers and typed arrays to provide output buffers. This works as long as the buffer only gets used while the native C function is being called. See [transient pointers](#transient-pointers) below for an example.
|
|
174
|
+
|
|
175
|
+
```{warning}
|
|
176
|
+
It is unsafe to keep the pointer around in the native code, or to change the contents outside of the function call where it is provided.
|
|
177
|
+
|
|
178
|
+
If you need to provide a pointer that will be kept around, allocate memory with [koffi.alloc()](#stable-pointers) instead.
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Transient pointers
|
|
182
|
+
|
|
183
|
+
*New in Koffi 2.3*
|
|
184
|
+
|
|
185
|
+
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.
|
|
186
|
+
|
|
187
|
+
Once the native function returns, you can decode the content with `koffi.decode(value, type)` as in the following example:
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
// ES6 syntax: import koffi from 'koffi';
|
|
191
|
+
const koffi = require('koffi');
|
|
192
|
+
|
|
193
|
+
const lib = koffi.load('libc.so.6');
|
|
194
|
+
|
|
195
|
+
const Vec3 = koffi.struct('Vec3', {
|
|
196
|
+
x: 'float32',
|
|
197
|
+
y: 'float32',
|
|
198
|
+
z: 'float32'
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
const memcpy = lib.func('void *memcpy(_Out_ void *dest, const void *src, size_t size)');
|
|
202
|
+
|
|
203
|
+
let vec1 = { x: 1, y: 2, z: 3 };
|
|
204
|
+
let vec2 = null;
|
|
205
|
+
|
|
206
|
+
// Copy the vector in a convoluted way through memcpy
|
|
207
|
+
{
|
|
208
|
+
let src = koffi.as(vec1, 'Vec3 *');
|
|
209
|
+
let dest = Buffer.allocUnsafe(koffi.sizeof(Vec3));
|
|
210
|
+
|
|
211
|
+
memcpy(dest, src, koffi.sizeof(Vec3));
|
|
212
|
+
|
|
213
|
+
vec2 = koffi.decode(dest, Vec3);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// CHange vector1, leaving copy alone
|
|
217
|
+
[vec1.x, vec1.y, vec1.z] = [vec1.z, vec1.y, vec1.x];
|
|
218
|
+
|
|
219
|
+
console.log(vec1); // { x: 3, y: 2, z: 1 }
|
|
220
|
+
console.log(vec2); // { x: 1, y: 2, z: 3 }
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
See [decoding variables](variables.md#decode-to-js-values) for more information about the decode function.
|
|
224
|
+
|
|
225
|
+
### Stable pointers
|
|
226
|
+
|
|
227
|
+
*New in Koffi 2.8*
|
|
228
|
+
|
|
229
|
+
In some cases, the native code may need to change the output buffer at a later time, maybe during a later call or from another thread.
|
|
230
|
+
|
|
231
|
+
In this case, it is **not safe to use buffers or typed arrays**!
|
|
232
|
+
|
|
233
|
+
However, you can use `koffi.alloc(type, len)` to allocate memory and get a pointer that won't move, and can be safely used at any time by the native code. Use [koffi.decode()](variables.md#decode-to-js-values) to read data from the pointer when needed.
|
|
234
|
+
|
|
235
|
+
The example below sets up some memory to be used as an output buffer where a concatenation function appends a string on each call.
|
|
236
|
+
|
|
237
|
+
```c
|
|
238
|
+
#include <assert.h>
|
|
239
|
+
#include <stddef.h>
|
|
240
|
+
|
|
241
|
+
static char *buf_ptr;
|
|
242
|
+
static size_t buf_len;
|
|
243
|
+
static size_t buf_size;
|
|
244
|
+
|
|
245
|
+
void reset_buffer(char *buf, size_t size)
|
|
246
|
+
{
|
|
247
|
+
assert(size > 1);
|
|
248
|
+
|
|
249
|
+
buf_ptr = buf;
|
|
250
|
+
buf_len = 0;
|
|
251
|
+
buf_size = size - 1; // Keep space for trailing NUL
|
|
252
|
+
|
|
253
|
+
buf_ptr[0] = 0;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
void append_str(const char *str)
|
|
257
|
+
{
|
|
258
|
+
for (size_t i = 0; str[i] && buf_len < buf_size; i++, buf_len++) {
|
|
259
|
+
buf_ptr[buf_len] = str[i];
|
|
260
|
+
}
|
|
261
|
+
buf_ptr[buf_len] = 0;
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
const reset_buffer = lib.func('void reset_buffer(char *buf, size_t size)');
|
|
267
|
+
const append_str = lib.func('void append_str(const char *str)');
|
|
268
|
+
|
|
269
|
+
let output = koffi.alloc('char', 64);
|
|
270
|
+
reset_buffer(output, 64);
|
|
271
|
+
|
|
272
|
+
append_str('Hello');
|
|
273
|
+
console.log(koffi.decode(output, 'char', -1)); // Prints Hello
|
|
274
|
+
|
|
275
|
+
append_str(' World!');
|
|
276
|
+
console.log(koffi.decode(output, 'char', -1)); // Prints Hello World!
|
|
277
|
+
```
|
package/doc/pointers.md
CHANGED
|
@@ -88,7 +88,7 @@ AddInt(sum, 6);
|
|
|
88
88
|
console.log(sum[0]); // Prints 42
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
###
|
|
91
|
+
### Dynamic arrays
|
|
92
92
|
|
|
93
93
|
In C, dynamically-sized arrays are usually passed around as pointers. The length is either passed as an additional argument, or inferred from the array content itself, for example with a terminating sentinel value (such as a NULL pointers in the case of an array of strings).
|
|
94
94
|
|
|
@@ -132,6 +132,69 @@ console.log(total); // Prints 14
|
|
|
132
132
|
|
|
133
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
|
+
## Handling void pointers
|
|
136
|
+
|
|
137
|
+
*New in Koffi 2.1*
|
|
138
|
+
|
|
139
|
+
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.
|
|
140
|
+
|
|
141
|
+
Koffi provides two features to deal with this:
|
|
142
|
+
|
|
143
|
+
- You can use `koffi.as(value, type)` to tell Koffi what kind of type is actually expected, as shown in the example below.
|
|
144
|
+
- Buffers and typed JS arrays can be used as values in place everywhere a pointer is expected. See [dynamic arrays](#dynamic-arrays) for more information, for input or output.
|
|
145
|
+
|
|
146
|
+
The example below shows the use of `koffi.as()` to read the header of a PNG file with `fread()` directly to a JS object.
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
// ES6 syntax: import koffi from 'koffi';
|
|
150
|
+
const koffi = require('koffi');
|
|
151
|
+
|
|
152
|
+
const lib = koffi.load('libc.so.6');
|
|
153
|
+
|
|
154
|
+
const FILE = koffi.opaque('FILE');
|
|
155
|
+
|
|
156
|
+
const PngHeader = koffi.pack('PngHeader', {
|
|
157
|
+
signature: koffi.array('uint8_t', 8),
|
|
158
|
+
ihdr: koffi.pack({
|
|
159
|
+
length: 'uint32_be_t',
|
|
160
|
+
chunk: koffi.array('char', 4),
|
|
161
|
+
width: 'uint32_be_t',
|
|
162
|
+
height: 'uint32_be_t',
|
|
163
|
+
depth: 'uint8_t',
|
|
164
|
+
color: 'uint8_t',
|
|
165
|
+
compression: 'uint8_t',
|
|
166
|
+
filter: 'uint8_t',
|
|
167
|
+
interlace: 'uint8_t',
|
|
168
|
+
crc: 'uint32_be_t'
|
|
169
|
+
})
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const fopen = lib.func('FILE *fopen(const char *path, const char *mode)');
|
|
173
|
+
const fclose = lib.func('int fclose(FILE *fp)');
|
|
174
|
+
const fread = lib.func('size_t fread(_Out_ void *ptr, size_t size, size_t nmemb, FILE *fp)');
|
|
175
|
+
|
|
176
|
+
let filename = process.argv[2];
|
|
177
|
+
if (filename == null)
|
|
178
|
+
throw new Error('Usage: node png.js <image.png>');
|
|
179
|
+
|
|
180
|
+
let hdr = {};
|
|
181
|
+
{
|
|
182
|
+
let fp = fopen(filename, 'rb');
|
|
183
|
+
if (!fp)
|
|
184
|
+
throw new Error(`Failed to open '${filename}'`);
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
let len = fread(koffi.as(hdr, 'PngHeader *'), 1, koffi.sizeof(PngHeader), fp);
|
|
188
|
+
if (len < koffi.sizeof(PngHeader))
|
|
189
|
+
throw new Error('Failed to read PNG header');
|
|
190
|
+
} finally {
|
|
191
|
+
fclose(fp);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log('PNG header:', hdr);
|
|
196
|
+
```
|
|
197
|
+
|
|
135
198
|
## Disposable types
|
|
136
199
|
|
|
137
200
|
*New in Koffi 2.0*
|
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.
|
|
382
|
-
stable: "2.
|
|
381
|
+
version: "2.8.0",
|
|
382
|
+
stable: "2.8.0",
|
|
383
383
|
description: "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
384
384
|
keywords: [
|
|
385
385
|
"foreign",
|
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.
|
|
382
|
-
stable: "2.
|
|
381
|
+
version: "2.8.0",
|
|
382
|
+
stable: "2.8.0",
|
|
383
383
|
description: "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
384
384
|
keywords: [
|
|
385
385
|
"foreign",
|
package/package.json
CHANGED
|
@@ -30,12 +30,16 @@ function(add_node_addon)
|
|
|
30
30
|
cmake_parse_arguments(ARG "" "NAME" "SOURCES" ${ARGN})
|
|
31
31
|
|
|
32
32
|
add_library(${ARG_NAME} SHARED ${ARG_SOURCES} ${NODE_JS_SOURCES})
|
|
33
|
+
target_link_node(${ARG_NAME})
|
|
33
34
|
set_target_properties(${ARG_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
endfunction()
|
|
36
|
+
|
|
37
|
+
function(target_link_node TARGET)
|
|
38
|
+
target_include_directories(${TARGET} PRIVATE ${NODE_JS_INCLUDE_DIRS})
|
|
39
|
+
target_link_libraries(${TARGET} PRIVATE ${NODE_JS_LIBRARIES})
|
|
40
|
+
target_compile_options(${TARGET} PRIVATE ${NODE_JS_COMPILE_FLAGS})
|
|
37
41
|
if (NODE_JS_LINK_FLAGS)
|
|
38
|
-
target_link_options(${
|
|
42
|
+
target_link_options(${TARGET} PRIVATE ${NODE_JS_LINK_FLAGS})
|
|
39
43
|
endif()
|
|
40
44
|
endfunction()
|
|
41
45
|
|
package/src/koffi/CMakeLists.txt
CHANGED
|
@@ -21,17 +21,16 @@
|
|
|
21
21
|
|
|
22
22
|
cmake_minimum_required(VERSION 3.6)
|
|
23
23
|
cmake_policy(SET CMP0091 NEW)
|
|
24
|
-
|
|
25
24
|
project(koffi C CXX ASM)
|
|
26
25
|
|
|
27
|
-
include(CheckCXXCompilerFlag)
|
|
28
|
-
|
|
29
26
|
find_package(CNoke)
|
|
30
27
|
|
|
28
|
+
include(CheckCXXCompilerFlag)
|
|
29
|
+
|
|
31
30
|
set(CMAKE_CXX_STANDARD 20)
|
|
32
31
|
if(MSVC)
|
|
33
32
|
set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
|
|
34
|
-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /W4 /wd4200 /wd4201 /wd4127 /wd4458 /wd4706 /wd4702 /wd4324")
|
|
33
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /Zc:preprocessor /W4 /wd4200 /wd4201 /wd4127 /wd4458 /wd4706 /wd4702 /wd4324")
|
|
35
34
|
|
|
36
35
|
# ASM_MASM does not (yet) work on Windows ARM64
|
|
37
36
|
if(NOT CMAKE_GENERATOR_PLATFORM MATCHES "ARM64")
|
package/src/koffi/src/call.cc
CHANGED
|
@@ -209,8 +209,7 @@ bool CallData::PushString(Napi::Value value, int directions, const char **out_st
|
|
|
209
209
|
|
|
210
210
|
return true;
|
|
211
211
|
} else {
|
|
212
|
-
|
|
213
|
-
return false;
|
|
212
|
+
return PushPointer(value, instance->str_type, directions, (void **)out_str);
|
|
214
213
|
}
|
|
215
214
|
}
|
|
216
215
|
|
|
@@ -311,8 +310,7 @@ bool CallData::PushString16(Napi::Value value, int directions, const char16_t **
|
|
|
311
310
|
|
|
312
311
|
return true;
|
|
313
312
|
} else {
|
|
314
|
-
|
|
315
|
-
return false;
|
|
313
|
+
return PushPointer(value, instance->str16_type, directions, (void **)out_str16);
|
|
316
314
|
}
|
|
317
315
|
}
|
|
318
316
|
|
|
@@ -983,7 +981,9 @@ bool CallData::PushPointer(Napi::Value value, const TypeInfo *type, int directio
|
|
|
983
981
|
} break;
|
|
984
982
|
|
|
985
983
|
case napi_external: {
|
|
986
|
-
RG_ASSERT(type->primitive == PrimitiveKind::Pointer
|
|
984
|
+
RG_ASSERT(type->primitive == PrimitiveKind::Pointer ||
|
|
985
|
+
type->primitive == PrimitiveKind::String ||
|
|
986
|
+
type->primitive == PrimitiveKind::String16);
|
|
987
987
|
|
|
988
988
|
if (!CheckValueTag(instance, value, type->ref.marker) &&
|
|
989
989
|
!CheckValueTag(instance, value, instance->void_type) &&
|
package/src/koffi/src/ffi.cc
CHANGED
|
@@ -770,6 +770,55 @@ static inline bool GetExternalPointer(Napi::Env env, Napi::Value value, void **o
|
|
|
770
770
|
}
|
|
771
771
|
}
|
|
772
772
|
|
|
773
|
+
static Napi::Value CallAlloc(const Napi::CallbackInfo &info)
|
|
774
|
+
{
|
|
775
|
+
Napi::Env env = info.Env();
|
|
776
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
777
|
+
|
|
778
|
+
if (info.Length() < 2) {
|
|
779
|
+
ThrowError<Napi::TypeError>(env, "Expected 2 arguments, got %1", info.Length());
|
|
780
|
+
return env.Null();
|
|
781
|
+
}
|
|
782
|
+
if (!info[1].IsNumber()) {
|
|
783
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for length, expected number", GetValueType(instance, info[1]));
|
|
784
|
+
return env.Null();
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const TypeInfo *type = ResolveType(info[0]);
|
|
788
|
+
if (!type)
|
|
789
|
+
return env.Null();
|
|
790
|
+
|
|
791
|
+
if (!type->size) [[unlikely]] {
|
|
792
|
+
ThrowError<Napi::TypeError>(env, "Cannot allocate memory for zero-sized type %1", type->name);
|
|
793
|
+
return env.Null();
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
int32_t len = info[1].As<Napi::Number>();
|
|
797
|
+
|
|
798
|
+
if (len <= 0) [[unlikely]] {
|
|
799
|
+
ThrowError<Napi::Error>(env, "Size must be greater than 0");
|
|
800
|
+
return env.Null();
|
|
801
|
+
}
|
|
802
|
+
if (len > INT32_MAX / type->size) [[unlikely]] {
|
|
803
|
+
ThrowError<Napi::Error>(env, "Cannot allocate more than %1 objects of type %2", INT32_MAX / type->size, type->name);
|
|
804
|
+
return env.Null();
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
void *ptr = calloc((size_t)len, (size_t)type->size);
|
|
808
|
+
|
|
809
|
+
if (!ptr) [[unlikely]] {
|
|
810
|
+
Size size = (Size)(len * type->size);
|
|
811
|
+
|
|
812
|
+
ThrowError<Napi::Error>(env, "Failed to allocate %1 of memory", FmtMemSize((Size)size));
|
|
813
|
+
return env.Null();
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
Napi::External<void> external = Napi::External<void>::New(env, ptr);
|
|
817
|
+
SetValueTag(instance, external, type);
|
|
818
|
+
|
|
819
|
+
return external;
|
|
820
|
+
}
|
|
821
|
+
|
|
773
822
|
static Napi::Value CallFree(const Napi::CallbackInfo &info)
|
|
774
823
|
{
|
|
775
824
|
Napi::Env env = info.Env();
|
|
@@ -1997,6 +2046,40 @@ static Napi::Value EncodeValue(const Napi::CallbackInfo &info)
|
|
|
1997
2046
|
return env.Undefined();
|
|
1998
2047
|
}
|
|
1999
2048
|
|
|
2049
|
+
static Napi::Value ResetKoffi(const Napi::CallbackInfo &info)
|
|
2050
|
+
{
|
|
2051
|
+
Napi::Env env = info.Env();
|
|
2052
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
2053
|
+
|
|
2054
|
+
if (instance->broker) {
|
|
2055
|
+
napi_release_threadsafe_function(instance->broker, napi_tsfn_abort);
|
|
2056
|
+
instance->broker = nullptr;
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
instance->types.RemoveFrom(instance->base_types_len);
|
|
2060
|
+
|
|
2061
|
+
{
|
|
2062
|
+
HashSet<const void *> base_types;
|
|
2063
|
+
HashMap<const char *, const TypeInfo *> new_map;
|
|
2064
|
+
|
|
2065
|
+
for (const TypeInfo &type: instance->types) {
|
|
2066
|
+
base_types.Set(&type);
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
for (const auto &bucket: instance->types_map.table) {
|
|
2070
|
+
if (base_types.Find(bucket.value)) {
|
|
2071
|
+
new_map.Set(bucket.key, bucket.value);
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
std::swap(instance->types_map, new_map);
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
instance->callbacks.Clear();
|
|
2079
|
+
|
|
2080
|
+
return env.Undefined();
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2000
2083
|
void LibraryHolder::Unload()
|
|
2001
2084
|
{
|
|
2002
2085
|
#ifdef _WIN32
|
|
@@ -2153,83 +2236,6 @@ static inline PrimitiveKind GetBigEndianPrimitive(PrimitiveKind kind)
|
|
|
2153
2236
|
#endif
|
|
2154
2237
|
}
|
|
2155
2238
|
|
|
2156
|
-
static Napi::Object InitBaseTypes(Napi::Env env)
|
|
2157
|
-
{
|
|
2158
|
-
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
2159
|
-
|
|
2160
|
-
Napi::Object types = Napi::Object::New(env);
|
|
2161
|
-
|
|
2162
|
-
RegisterPrimitiveType(env, types, {"void"}, PrimitiveKind::Void, 0, 0);
|
|
2163
|
-
RegisterPrimitiveType(env, types, {"bool"}, PrimitiveKind::Bool, RG_SIZE(bool), alignof(bool));
|
|
2164
|
-
RegisterPrimitiveType(env, types, {"int8_t", "int8"}, PrimitiveKind::Int8, 1, 1);
|
|
2165
|
-
RegisterPrimitiveType(env, types, {"uint8_t", "uint8"}, PrimitiveKind::UInt8, 1, 1);
|
|
2166
|
-
RegisterPrimitiveType(env, types, {"char"}, PrimitiveKind::Int8, 1, 1);
|
|
2167
|
-
RegisterPrimitiveType(env, types, {"unsigned char", "uchar"}, PrimitiveKind::UInt8, 1, 1);
|
|
2168
|
-
RegisterPrimitiveType(env, types, {"char16_t", "char16"}, PrimitiveKind::Int16, 2, 2);
|
|
2169
|
-
RegisterPrimitiveType(env, types, {"int16_t", "int16"}, PrimitiveKind::Int16, 2, 2);
|
|
2170
|
-
RegisterPrimitiveType(env, types, {"int16_le_t", "int16_le"}, GetLittleEndianPrimitive(PrimitiveKind::Int16), 2, 2);
|
|
2171
|
-
RegisterPrimitiveType(env, types, {"int16_be_t", "int16_be"}, GetBigEndianPrimitive(PrimitiveKind::Int16), 2, 2);
|
|
2172
|
-
RegisterPrimitiveType(env, types, {"uint16_t", "uint16"}, PrimitiveKind::UInt16, 2, 2);
|
|
2173
|
-
RegisterPrimitiveType(env, types, {"uint16_le_t", "uint16_le"}, GetLittleEndianPrimitive(PrimitiveKind::UInt16), 2, 2);
|
|
2174
|
-
RegisterPrimitiveType(env, types, {"uint16_be_t", "uint16_be"}, GetBigEndianPrimitive(PrimitiveKind::UInt16), 2, 2);
|
|
2175
|
-
RegisterPrimitiveType(env, types, {"short"}, PrimitiveKind::Int16, 2, 2);
|
|
2176
|
-
RegisterPrimitiveType(env, types, {"unsigned short", "ushort"}, PrimitiveKind::UInt16, 2, 2);
|
|
2177
|
-
RegisterPrimitiveType(env, types, {"int32_t", "int32"}, PrimitiveKind::Int32, 4, 4);
|
|
2178
|
-
RegisterPrimitiveType(env, types, {"int32_le_t", "int32_le"}, GetLittleEndianPrimitive(PrimitiveKind::Int32), 4, 4);
|
|
2179
|
-
RegisterPrimitiveType(env, types, {"int32_be_t", "int32_be"}, GetBigEndianPrimitive(PrimitiveKind::Int32), 4, 4);
|
|
2180
|
-
RegisterPrimitiveType(env, types, {"uint32_t", "uint32"}, PrimitiveKind::UInt32, 4, 4);
|
|
2181
|
-
RegisterPrimitiveType(env, types, {"uint32_le_t", "uint32_le"}, GetLittleEndianPrimitive(PrimitiveKind::UInt32), 4, 4);
|
|
2182
|
-
RegisterPrimitiveType(env, types, {"uint32_be_t", "uint32_be"}, GetBigEndianPrimitive(PrimitiveKind::UInt32), 4, 4);
|
|
2183
|
-
RegisterPrimitiveType(env, types, {"int"}, PrimitiveKind::Int32, 4, 4);
|
|
2184
|
-
RegisterPrimitiveType(env, types, {"unsigned int", "uint"}, PrimitiveKind::UInt32, 4, 4);
|
|
2185
|
-
RegisterPrimitiveType(env, types, {"int64_t", "int64"}, PrimitiveKind::Int64, 8, alignof(int64_t));
|
|
2186
|
-
RegisterPrimitiveType(env, types, {"int64_le_t", "int64_le"}, GetLittleEndianPrimitive(PrimitiveKind::Int64), 8, alignof(int64_t));
|
|
2187
|
-
RegisterPrimitiveType(env, types, {"int64_be_t", "int64_be"}, GetBigEndianPrimitive(PrimitiveKind::Int64), 8, alignof(int64_t));
|
|
2188
|
-
RegisterPrimitiveType(env, types, {"uint64_t", "uint64"}, PrimitiveKind::UInt64, 8, alignof(int64_t));
|
|
2189
|
-
RegisterPrimitiveType(env, types, {"uint64_le_t", "uint64_le"}, GetLittleEndianPrimitive(PrimitiveKind::UInt64), 8, alignof(int64_t));
|
|
2190
|
-
RegisterPrimitiveType(env, types, {"uint64_be_t", "uint64_be"}, GetBigEndianPrimitive(PrimitiveKind::UInt64), 8, alignof(int64_t));
|
|
2191
|
-
RegisterPrimitiveType(env, types, {"intptr_t", "intptr"}, GetSignPrimitive(RG_SIZE(intptr_t), true), RG_SIZE(intptr_t), alignof(intptr_t));
|
|
2192
|
-
RegisterPrimitiveType(env, types, {"uintptr_t", "uintptr"}, GetSignPrimitive(RG_SIZE(intptr_t), false), RG_SIZE(intptr_t), alignof(intptr_t));
|
|
2193
|
-
RegisterPrimitiveType(env, types, {"size_t"}, GetSignPrimitive(RG_SIZE(size_t), false), RG_SIZE(size_t), alignof(size_t));
|
|
2194
|
-
RegisterPrimitiveType(env, types, {"long"}, GetSignPrimitive(RG_SIZE(long), true), RG_SIZE(long), alignof(long));
|
|
2195
|
-
RegisterPrimitiveType(env, types, {"unsigned long", "ulong"}, GetSignPrimitive(RG_SIZE(long), false), RG_SIZE(long), alignof(long));
|
|
2196
|
-
RegisterPrimitiveType(env, types, {"long long", "longlong"}, PrimitiveKind::Int64, RG_SIZE(int64_t), alignof(int64_t));
|
|
2197
|
-
RegisterPrimitiveType(env, types, {"unsigned long long", "ulonglong"}, PrimitiveKind::UInt64, RG_SIZE(uint64_t), alignof(uint64_t));
|
|
2198
|
-
RegisterPrimitiveType(env, types, {"float", "float32"}, PrimitiveKind::Float32, 4, alignof(float));
|
|
2199
|
-
RegisterPrimitiveType(env, types, {"double", "float64"}, PrimitiveKind::Float64, 8, alignof(double));
|
|
2200
|
-
RegisterPrimitiveType(env, types, {"char *", "str", "string"}, PrimitiveKind::String, RG_SIZE(void *), alignof(void *), "char");
|
|
2201
|
-
RegisterPrimitiveType(env, types, {"char16_t *", "char16 *", "str16", "string16"}, PrimitiveKind::String16, RG_SIZE(void *), alignof(void *), "char16_t");
|
|
2202
|
-
|
|
2203
|
-
instance->void_type = instance->types_map.FindValue("void", nullptr);
|
|
2204
|
-
instance->char_type = instance->types_map.FindValue("char", nullptr);
|
|
2205
|
-
instance->char16_type = instance->types_map.FindValue("char16", nullptr);
|
|
2206
|
-
|
|
2207
|
-
instance->active_symbol = Napi::Symbol::New(env, "active");
|
|
2208
|
-
|
|
2209
|
-
types.Freeze();
|
|
2210
|
-
|
|
2211
|
-
return types;
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
|
-
static Napi::Value ResetKoffi(const Napi::CallbackInfo &info)
|
|
2215
|
-
{
|
|
2216
|
-
Napi::Env env = info.Env();
|
|
2217
|
-
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
2218
|
-
|
|
2219
|
-
if (instance->broker) {
|
|
2220
|
-
napi_release_threadsafe_function(instance->broker, napi_tsfn_abort);
|
|
2221
|
-
instance->broker = nullptr;
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
|
-
instance->types.Clear();
|
|
2225
|
-
instance->types_map.Clear();
|
|
2226
|
-
instance->callbacks.Clear();
|
|
2227
|
-
|
|
2228
|
-
InitBaseTypes(env);
|
|
2229
|
-
|
|
2230
|
-
return env.Undefined();
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
2239
|
static InstanceData *CreateInstance()
|
|
2234
2240
|
{
|
|
2235
2241
|
InstanceData *instance = new InstanceData();
|
|
@@ -2258,52 +2264,53 @@ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
|
|
|
2258
2264
|
|
|
2259
2265
|
env.SetInstanceData(instance);
|
|
2260
2266
|
|
|
2261
|
-
exports.Set("config", Napi::Function::New(env, GetSetConfig));
|
|
2262
|
-
exports.Set("stats", Napi::Function::New(env, GetStats));
|
|
2263
|
-
|
|
2264
|
-
exports.Set("struct", Napi::Function::New(env, CreatePaddedStructType));
|
|
2265
|
-
exports.Set("pack", Napi::Function::New(env, CreatePackedStructType));
|
|
2266
|
-
exports.Set("union", Napi::Function::New(env, CreateUnionType));
|
|
2267
|
-
exports.Set("Union", Napi::Function::New(env, InstantiateUnion));
|
|
2268
|
-
exports.Set("opaque", Napi::Function::New(env, CreateOpaqueType));
|
|
2269
|
-
exports.Set("pointer", Napi::Function::New(env, CreatePointerType));
|
|
2270
|
-
exports.Set("array", Napi::Function::New(env, CreateArrayType));
|
|
2271
|
-
exports.Set("proto", Napi::Function::New(env, CreateFunctionType));
|
|
2272
|
-
exports.Set("alias", Napi::Function::New(env, CreateTypeAlias));
|
|
2267
|
+
exports.Set("config", Napi::Function::New(env, GetSetConfig, "config"));
|
|
2268
|
+
exports.Set("stats", Napi::Function::New(env, GetStats, "stats"));
|
|
2273
2269
|
|
|
2274
|
-
exports.Set("
|
|
2275
|
-
exports.Set("
|
|
2276
|
-
exports.Set("
|
|
2277
|
-
exports.Set("
|
|
2278
|
-
exports.Set("
|
|
2270
|
+
exports.Set("struct", Napi::Function::New(env, CreatePaddedStructType, "struct"));
|
|
2271
|
+
exports.Set("pack", Napi::Function::New(env, CreatePackedStructType, "pack"));
|
|
2272
|
+
exports.Set("union", Napi::Function::New(env, CreateUnionType, "union"));
|
|
2273
|
+
exports.Set("Union", Napi::Function::New(env, InstantiateUnion, "Union"));
|
|
2274
|
+
exports.Set("opaque", Napi::Function::New(env, CreateOpaqueType, "opaque"));
|
|
2275
|
+
exports.Set("pointer", Napi::Function::New(env, CreatePointerType, "pointer"));
|
|
2276
|
+
exports.Set("array", Napi::Function::New(env, CreateArrayType, "array"));
|
|
2277
|
+
exports.Set("proto", Napi::Function::New(env, CreateFunctionType, "proto"));
|
|
2278
|
+
exports.Set("alias", Napi::Function::New(env, CreateTypeAlias, "alias"));
|
|
2279
2279
|
|
|
2280
|
-
exports.Set("
|
|
2280
|
+
exports.Set("sizeof", Napi::Function::New(env, GetTypeSize, "sizeof"));
|
|
2281
|
+
exports.Set("alignof", Napi::Function::New(env, GetTypeAlign, "alignof"));
|
|
2282
|
+
exports.Set("offsetof", Napi::Function::New(env, GetMemberOffset, "offsetof"));
|
|
2283
|
+
exports.Set("resolve", Napi::Function::New(env, GetResolvedType, "resolve"));
|
|
2284
|
+
exports.Set("introspect", Napi::Function::New(env, GetTypeDefinition, "introspect"));
|
|
2281
2285
|
|
|
2282
|
-
exports.Set("
|
|
2283
|
-
exports.Set("out", Napi::Function::New(env, MarkOut));
|
|
2284
|
-
exports.Set("inout", Napi::Function::New(env, MarkInOut));
|
|
2286
|
+
exports.Set("load", Napi::Function::New(env, LoadSharedLibrary, "load"));
|
|
2285
2287
|
|
|
2286
|
-
exports.Set("
|
|
2287
|
-
exports.Set("
|
|
2288
|
+
exports.Set("in", Napi::Function::New(env, MarkIn, "in"));
|
|
2289
|
+
exports.Set("out", Napi::Function::New(env, MarkOut, "out"));
|
|
2290
|
+
exports.Set("inout", Napi::Function::New(env, MarkInOut, "inout"));
|
|
2288
2291
|
|
|
2289
|
-
exports.Set("
|
|
2290
|
-
exports.Set("
|
|
2292
|
+
exports.Set("disposable", Napi::Function::New(env, CreateDisposableType, "disposable"));
|
|
2293
|
+
exports.Set("alloc", Napi::Function::New(env, CallAlloc, "alloc"));
|
|
2294
|
+
exports.Set("free", Napi::Function::New(env, CallFree, "free"));
|
|
2291
2295
|
|
|
2292
|
-
exports.Set("
|
|
2293
|
-
exports.Set("
|
|
2294
|
-
exports.Set("address", Napi::Function::New(env, GetPointerAddress));
|
|
2295
|
-
exports.Set("call", Napi::Function::New(env, CallPointerSync));
|
|
2296
|
-
exports.Set("encode", Napi::Function::New(env, EncodeValue));
|
|
2296
|
+
exports.Set("register", Napi::Function::New(env, RegisterCallback, "register"));
|
|
2297
|
+
exports.Set("unregister", Napi::Function::New(env, UnregisterCallback, "unregister"));
|
|
2297
2298
|
|
|
2298
|
-
exports.Set("
|
|
2299
|
+
exports.Set("as", Napi::Function::New(env, CastValue, "as"));
|
|
2300
|
+
exports.Set("decode", Napi::Function::New(env, DecodeValue, "decode"));
|
|
2301
|
+
exports.Set("address", Napi::Function::New(env, GetPointerAddress, "address"));
|
|
2302
|
+
exports.Set("call", Napi::Function::New(env, CallPointerSync, "call"));
|
|
2303
|
+
exports.Set("encode", Napi::Function::New(env, EncodeValue, "encode"));
|
|
2299
2304
|
|
|
2300
|
-
exports.Set("
|
|
2305
|
+
exports.Set("reset", Napi::Function::New(env, ResetKoffi, "reset"));
|
|
2301
2306
|
|
|
2302
|
-
|
|
2303
|
-
exports.Set("os", os);
|
|
2307
|
+
exports.Set("errno", Napi::Function::New(env, GetOrSetErrNo, "errno"));
|
|
2304
2308
|
|
|
2305
|
-
//
|
|
2309
|
+
// Export useful OS info
|
|
2306
2310
|
{
|
|
2311
|
+
Napi::Object os = Napi::Object::New(env);
|
|
2312
|
+
exports.Set("os", os);
|
|
2313
|
+
|
|
2307
2314
|
Napi::Object codes = Napi::Object::New(env);
|
|
2308
2315
|
|
|
2309
2316
|
for (const ErrnoCodeInfo &info: ErrnoCodes) {
|
|
@@ -2321,8 +2328,73 @@ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
|
|
|
2321
2328
|
exports.Set("extension", Napi::String::New(env, ".so"));
|
|
2322
2329
|
#endif
|
|
2323
2330
|
|
|
2324
|
-
|
|
2325
|
-
|
|
2331
|
+
// Init base types
|
|
2332
|
+
{
|
|
2333
|
+
Napi::Object types = Napi::Object::New(env);
|
|
2334
|
+
exports.Set("types", types);
|
|
2335
|
+
|
|
2336
|
+
RegisterPrimitiveType(env, types, {"void"}, PrimitiveKind::Void, 0, 0);
|
|
2337
|
+
RegisterPrimitiveType(env, types, {"bool"}, PrimitiveKind::Bool, RG_SIZE(bool), alignof(bool));
|
|
2338
|
+
RegisterPrimitiveType(env, types, {"int8_t", "int8"}, PrimitiveKind::Int8, 1, 1);
|
|
2339
|
+
RegisterPrimitiveType(env, types, {"uint8_t", "uint8"}, PrimitiveKind::UInt8, 1, 1);
|
|
2340
|
+
RegisterPrimitiveType(env, types, {"char"}, PrimitiveKind::Int8, 1, 1);
|
|
2341
|
+
RegisterPrimitiveType(env, types, {"unsigned char", "uchar"}, PrimitiveKind::UInt8, 1, 1);
|
|
2342
|
+
RegisterPrimitiveType(env, types, {"char16_t", "char16"}, PrimitiveKind::Int16, 2, 2);
|
|
2343
|
+
RegisterPrimitiveType(env, types, {"int16_t", "int16"}, PrimitiveKind::Int16, 2, 2);
|
|
2344
|
+
RegisterPrimitiveType(env, types, {"int16_le_t", "int16_le"}, GetLittleEndianPrimitive(PrimitiveKind::Int16), 2, 2);
|
|
2345
|
+
RegisterPrimitiveType(env, types, {"int16_be_t", "int16_be"}, GetBigEndianPrimitive(PrimitiveKind::Int16), 2, 2);
|
|
2346
|
+
RegisterPrimitiveType(env, types, {"uint16_t", "uint16"}, PrimitiveKind::UInt16, 2, 2);
|
|
2347
|
+
RegisterPrimitiveType(env, types, {"uint16_le_t", "uint16_le"}, GetLittleEndianPrimitive(PrimitiveKind::UInt16), 2, 2);
|
|
2348
|
+
RegisterPrimitiveType(env, types, {"uint16_be_t", "uint16_be"}, GetBigEndianPrimitive(PrimitiveKind::UInt16), 2, 2);
|
|
2349
|
+
RegisterPrimitiveType(env, types, {"short"}, PrimitiveKind::Int16, 2, 2);
|
|
2350
|
+
RegisterPrimitiveType(env, types, {"unsigned short", "ushort"}, PrimitiveKind::UInt16, 2, 2);
|
|
2351
|
+
RegisterPrimitiveType(env, types, {"int32_t", "int32"}, PrimitiveKind::Int32, 4, 4);
|
|
2352
|
+
RegisterPrimitiveType(env, types, {"int32_le_t", "int32_le"}, GetLittleEndianPrimitive(PrimitiveKind::Int32), 4, 4);
|
|
2353
|
+
RegisterPrimitiveType(env, types, {"int32_be_t", "int32_be"}, GetBigEndianPrimitive(PrimitiveKind::Int32), 4, 4);
|
|
2354
|
+
RegisterPrimitiveType(env, types, {"uint32_t", "uint32"}, PrimitiveKind::UInt32, 4, 4);
|
|
2355
|
+
RegisterPrimitiveType(env, types, {"uint32_le_t", "uint32_le"}, GetLittleEndianPrimitive(PrimitiveKind::UInt32), 4, 4);
|
|
2356
|
+
RegisterPrimitiveType(env, types, {"uint32_be_t", "uint32_be"}, GetBigEndianPrimitive(PrimitiveKind::UInt32), 4, 4);
|
|
2357
|
+
RegisterPrimitiveType(env, types, {"int"}, PrimitiveKind::Int32, 4, 4);
|
|
2358
|
+
RegisterPrimitiveType(env, types, {"unsigned int", "uint"}, PrimitiveKind::UInt32, 4, 4);
|
|
2359
|
+
RegisterPrimitiveType(env, types, {"int64_t", "int64"}, PrimitiveKind::Int64, 8, alignof(int64_t));
|
|
2360
|
+
RegisterPrimitiveType(env, types, {"int64_le_t", "int64_le"}, GetLittleEndianPrimitive(PrimitiveKind::Int64), 8, alignof(int64_t));
|
|
2361
|
+
RegisterPrimitiveType(env, types, {"int64_be_t", "int64_be"}, GetBigEndianPrimitive(PrimitiveKind::Int64), 8, alignof(int64_t));
|
|
2362
|
+
RegisterPrimitiveType(env, types, {"uint64_t", "uint64"}, PrimitiveKind::UInt64, 8, alignof(int64_t));
|
|
2363
|
+
RegisterPrimitiveType(env, types, {"uint64_le_t", "uint64_le"}, GetLittleEndianPrimitive(PrimitiveKind::UInt64), 8, alignof(int64_t));
|
|
2364
|
+
RegisterPrimitiveType(env, types, {"uint64_be_t", "uint64_be"}, GetBigEndianPrimitive(PrimitiveKind::UInt64), 8, alignof(int64_t));
|
|
2365
|
+
RegisterPrimitiveType(env, types, {"intptr_t", "intptr"}, GetSignPrimitive(RG_SIZE(intptr_t), true), RG_SIZE(intptr_t), alignof(intptr_t));
|
|
2366
|
+
RegisterPrimitiveType(env, types, {"uintptr_t", "uintptr"}, GetSignPrimitive(RG_SIZE(intptr_t), false), RG_SIZE(intptr_t), alignof(intptr_t));
|
|
2367
|
+
RegisterPrimitiveType(env, types, {"size_t"}, GetSignPrimitive(RG_SIZE(size_t), false), RG_SIZE(size_t), alignof(size_t));
|
|
2368
|
+
RegisterPrimitiveType(env, types, {"long"}, GetSignPrimitive(RG_SIZE(long), true), RG_SIZE(long), alignof(long));
|
|
2369
|
+
RegisterPrimitiveType(env, types, {"unsigned long", "ulong"}, GetSignPrimitive(RG_SIZE(long), false), RG_SIZE(long), alignof(long));
|
|
2370
|
+
RegisterPrimitiveType(env, types, {"long long", "longlong"}, PrimitiveKind::Int64, RG_SIZE(int64_t), alignof(int64_t));
|
|
2371
|
+
RegisterPrimitiveType(env, types, {"unsigned long long", "ulonglong"}, PrimitiveKind::UInt64, RG_SIZE(uint64_t), alignof(uint64_t));
|
|
2372
|
+
RegisterPrimitiveType(env, types, {"float", "float32"}, PrimitiveKind::Float32, 4, alignof(float));
|
|
2373
|
+
RegisterPrimitiveType(env, types, {"double", "float64"}, PrimitiveKind::Float64, 8, alignof(double));
|
|
2374
|
+
RegisterPrimitiveType(env, types, {"char *", "str", "string"}, PrimitiveKind::String, RG_SIZE(void *), alignof(void *), "char");
|
|
2375
|
+
RegisterPrimitiveType(env, types, {"char16_t *", "char16 *", "str16", "string16"}, PrimitiveKind::String16, RG_SIZE(void *), alignof(void *), "char16_t");
|
|
2376
|
+
|
|
2377
|
+
instance->void_type = instance->types_map.FindValue("void", nullptr);
|
|
2378
|
+
instance->char_type = instance->types_map.FindValue("char", nullptr);
|
|
2379
|
+
instance->char16_type = instance->types_map.FindValue("char16", nullptr);
|
|
2380
|
+
instance->str_type = instance->types_map.FindValue("char *", nullptr);
|
|
2381
|
+
instance->str16_type = instance->types_map.FindValue("char16_t *", nullptr);
|
|
2382
|
+
|
|
2383
|
+
instance->active_symbol = Napi::Symbol::New(env, "active");
|
|
2384
|
+
|
|
2385
|
+
instance->base_types_len = instance->types.len;
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
// Expose internal Node stuff
|
|
2389
|
+
{
|
|
2390
|
+
Napi::Object node = Napi::Object::New(env);
|
|
2391
|
+
exports.Set("node", node);
|
|
2392
|
+
|
|
2393
|
+
Napi::External<void> external = Napi::External<void>::New(env, (napi_env)env);
|
|
2394
|
+
SetValueTag(instance, external, instance->void_type);
|
|
2395
|
+
|
|
2396
|
+
node.Set("env", external);
|
|
2397
|
+
}
|
|
2326
2398
|
|
|
2327
2399
|
exports.Set("version", Napi::String::New(env, RG_STRINGIFY(VERSION)));
|
|
2328
2400
|
|
package/src/koffi/src/ffi.hh
CHANGED
|
@@ -269,6 +269,7 @@ struct InstanceData {
|
|
|
269
269
|
BucketArray<TypeInfo> types;
|
|
270
270
|
HashMap<const char *, const TypeInfo *> types_map;
|
|
271
271
|
BucketArray<FunctionInfo> callbacks;
|
|
272
|
+
Size base_types_len;
|
|
272
273
|
|
|
273
274
|
bool debug;
|
|
274
275
|
uint64_t tag_lower;
|
|
@@ -276,6 +277,8 @@ struct InstanceData {
|
|
|
276
277
|
const TypeInfo *void_type;
|
|
277
278
|
const TypeInfo *char_type;
|
|
278
279
|
const TypeInfo *char16_type;
|
|
280
|
+
const TypeInfo *str_type;
|
|
281
|
+
const TypeInfo *str16_type;
|
|
279
282
|
|
|
280
283
|
Napi::Symbol active_symbol;
|
|
281
284
|
|
package/doc/polymorphism.md
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
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 [decoding variables](variables.md#decode-to-js-values) for more information about the decode function.
|