koffi 2.14.0-beta.3 → 2.14.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 +11 -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_arm64/koffi.node +0 -0
- package/build/koffi/linux_armhf/koffi.node +0 -0
- package/build/koffi/linux_ia32/koffi.node +0 -0
- package/build/koffi/linux_loong64/koffi.node +0 -0
- package/build/koffi/linux_riscv64d/koffi.node +0 -0
- package/build/koffi/linux_x64/koffi.node +0 -0
- package/build/koffi/musl_arm64/koffi.node +0 -0
- package/build/koffi/musl_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/pages/input.md +66 -0
- package/index.d.ts +0 -1
- package/index.js +8 -8
- package/indirect.js +8 -8
- package/package.json +1 -1
- package/src/core/base/base.cc +25 -0
- package/src/core/base/base.hh +1 -0
- package/src/koffi/src/call.cc +1 -1
- package/src/koffi/src/call.hh +2 -2
- package/src/koffi/src/ffi.cc +11 -7
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
|
|
6
6
|
## Koffi 2
|
|
7
7
|
|
|
8
|
+
### Koffi 2.14
|
|
9
|
+
|
|
10
|
+
#### Koffi 2.14.0
|
|
11
|
+
|
|
12
|
+
*Released on 2025-08-17*
|
|
13
|
+
|
|
14
|
+
- Improve support for structs with [flexible array member](input#flexible-arrays)
|
|
15
|
+
- Automatically encode/decode dynamic arrays pointers when length is known though struct member
|
|
16
|
+
- Fix parser crash when direction qualifier is followed by unknown type
|
|
17
|
+
- Add missing TypeScript types and arguments
|
|
18
|
+
|
|
8
19
|
### Koffi 2.13
|
|
9
20
|
|
|
10
21
|
#### Koffi 2.13.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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/doc/pages/input.md
CHANGED
|
@@ -396,6 +396,72 @@ Koffi can also convert JS strings to fixed-sized arrays in the following cases:
|
|
|
396
396
|
|
|
397
397
|
The reverse case is also true, Koffi can convert a C fixed-size buffer to a JS string. This happens by default for char, char16_t and char32_t arrays, but you can also explicitly ask for this with the `String` array hint (e.g. `koffi.array('char', 8, 'String')`).
|
|
398
398
|
|
|
399
|
+
## Flexible arrays
|
|
400
|
+
|
|
401
|
+
*Added in Koffi 2.14.0*
|
|
402
|
+
|
|
403
|
+
C structs ending with a flexible array member are often used for variable-sized structs, and are generally paired with dynamic memory allocation. In many cases, the number of elements is described by another struct member.
|
|
404
|
+
|
|
405
|
+
Use `koffi.array(type, countedBy, maxLen)` to make a flexible array type, for which the array length is determined by the struct member indicated by the `countedBy` parameter. Flexible array types can only be used as the last member of a struct, as shown below:
|
|
406
|
+
|
|
407
|
+
```js
|
|
408
|
+
const FlexibleArray = koffi.struct('FlexibleArray', {
|
|
409
|
+
count: 'size_t',
|
|
410
|
+
numbers: koffi.array('int', 'count', 128)
|
|
411
|
+
});
|
|
412
|
+
````
|
|
413
|
+
|
|
414
|
+
For various reasons, Koffi requires you to specify an upper bound (`maxLen`) for the number of elements in the flexible array member.
|
|
415
|
+
|
|
416
|
+
> [!WARNING]
|
|
417
|
+
> Also, unlike C flexible arrays, the struct size will expand to accomodate the maximum size of the flexible array.
|
|
418
|
+
|
|
419
|
+
The following example illustrates how to use a flexible array API in C from Koffi.
|
|
420
|
+
|
|
421
|
+
```c
|
|
422
|
+
// Build with: clang -fPIC -o flexible.so -shared flexible.c -Wall -O2
|
|
423
|
+
|
|
424
|
+
#include <stddef.h>
|
|
425
|
+
|
|
426
|
+
struct FlexibleArray {
|
|
427
|
+
size_t count;
|
|
428
|
+
int numbers[];
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
void AppendValues(struct FlexibleArray *arr, size_t count, int start, int step)
|
|
432
|
+
{
|
|
433
|
+
for (size_t i = 0; i < count; i++) {
|
|
434
|
+
arr->numbers[arr->count + i] = start + i * step;
|
|
435
|
+
}
|
|
436
|
+
arr->count += count;
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
```js
|
|
441
|
+
// ES6 syntax: import koffi from 'koffi';
|
|
442
|
+
const koffi = require('koffi');
|
|
443
|
+
|
|
444
|
+
const lib = koffi.load('./flexible.so');
|
|
445
|
+
|
|
446
|
+
const FlexibleArray = koffi.struct('FlexibleArray', {
|
|
447
|
+
count: 'size_t',
|
|
448
|
+
numbers: koffi.array('int', 'count', 256, 'Array')
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
const AppendValues = lib.func('void AppendValues(_Inout_ FlexibleArray *arr, int count, int start, int step)');
|
|
452
|
+
|
|
453
|
+
let array = { count: 0, numbers: [] };
|
|
454
|
+
|
|
455
|
+
AppendValues(array, 5, 1, 1);
|
|
456
|
+
console.log(array); // Prints { count: 5, numbers: [1, 2, 3, 4, 5] }
|
|
457
|
+
|
|
458
|
+
AppendValues(array, 3, 10, 2);
|
|
459
|
+
console.log(array); // Prints { count: 8, numbers: [1, 2, 3, 4, 5, 10, 12, 14] }
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
> [!NOTE]
|
|
463
|
+
> This is frequently used in the Win32 API, an exemple of this is [AllocateAndInitializeSid()](https://learn.microsoft.com/windows/win32/api/securitybaseapi/nf-securitybaseapi-allocateandinitializesid).
|
|
464
|
+
|
|
399
465
|
## Dynamic arrays (pointers)
|
|
400
466
|
|
|
401
467
|
In C, dynamically-sized arrays are usually passed around as pointers. Read more about [array pointers](pointers#dynamic-arrays) in the relevant section.
|
package/index.d.ts
CHANGED
|
@@ -105,7 +105,6 @@ export class Union {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
export function array(ref: TypeSpec, len: number, hint?: ArrayHint | null): IKoffiCType;
|
|
108
|
-
export function array(ref: TypeSpec, countedBy: string, hint?: ArrayHint | null): IKoffiCType;
|
|
109
108
|
export function array(ref: TypeSpec, countedBy: string, maxLen: number, hint?: ArrayHint | null): IKoffiCType;
|
|
110
109
|
|
|
111
110
|
export function opaque(name: string | null | undefined): IKoffiCType;
|
package/index.js
CHANGED
|
@@ -4,9 +4,9 @@ var __commonJS = (cb, mod3) => function __require() {
|
|
|
4
4
|
return mod3 || (0, cb[__getOwnPropNames(cb)[0]])((mod3 = { exports: {} }).exports, mod3), mod3.exports;
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
// bin/Koffi/package/src/cnoke/src/tools.js
|
|
7
|
+
// ../../../bin/Koffi/package/src/cnoke/src/tools.js
|
|
8
8
|
var require_tools = __commonJS({
|
|
9
|
-
"bin/Koffi/package/src/cnoke/src/tools.js"(exports2, module2) {
|
|
9
|
+
"../../../bin/Koffi/package/src/cnoke/src/tools.js"(exports2, module2) {
|
|
10
10
|
"use strict";
|
|
11
11
|
var crypto = require("crypto");
|
|
12
12
|
var fs2 = require("fs");
|
|
@@ -397,12 +397,12 @@ var require_tools = __commonJS({
|
|
|
397
397
|
}
|
|
398
398
|
});
|
|
399
399
|
|
|
400
|
-
// bin/Koffi/package/src/koffi/package.json
|
|
400
|
+
// ../../../bin/Koffi/package/src/koffi/package.json
|
|
401
401
|
var require_package = __commonJS({
|
|
402
|
-
"bin/Koffi/package/src/koffi/package.json"(exports2, module2) {
|
|
402
|
+
"../../../bin/Koffi/package/src/koffi/package.json"(exports2, module2) {
|
|
403
403
|
module2.exports = {
|
|
404
404
|
name: "koffi",
|
|
405
|
-
version: "2.14.0
|
|
405
|
+
version: "2.14.0",
|
|
406
406
|
description: "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
407
407
|
keywords: [
|
|
408
408
|
"foreign",
|
|
@@ -443,9 +443,9 @@ var require_package = __commonJS({
|
|
|
443
443
|
}
|
|
444
444
|
});
|
|
445
445
|
|
|
446
|
-
// bin/Koffi/package/src/koffi/src/init.js
|
|
446
|
+
// ../../../bin/Koffi/package/src/koffi/src/init.js
|
|
447
447
|
var require_init = __commonJS({
|
|
448
|
-
"bin/Koffi/package/src/koffi/src/init.js"(exports, module) {
|
|
448
|
+
"../../../bin/Koffi/package/src/koffi/src/init.js"(exports, module) {
|
|
449
449
|
var fs = require("fs");
|
|
450
450
|
var path = require("path");
|
|
451
451
|
var util = require("util");
|
|
@@ -528,7 +528,7 @@ var require_init = __commonJS({
|
|
|
528
528
|
}
|
|
529
529
|
});
|
|
530
530
|
|
|
531
|
-
// bin/Koffi/package/src/koffi/index.js
|
|
531
|
+
// ../../../bin/Koffi/package/src/koffi/index.js
|
|
532
532
|
var { detect: detect2, init: init2 } = require_init();
|
|
533
533
|
var triplet2 = detect2();
|
|
534
534
|
var native2 = null;
|
package/indirect.js
CHANGED
|
@@ -4,9 +4,9 @@ var __commonJS = (cb, mod3) => function __require() {
|
|
|
4
4
|
return mod3 || (0, cb[__getOwnPropNames(cb)[0]])((mod3 = { exports: {} }).exports, mod3), mod3.exports;
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
// bin/Koffi/package/src/cnoke/src/tools.js
|
|
7
|
+
// ../../../bin/Koffi/package/src/cnoke/src/tools.js
|
|
8
8
|
var require_tools = __commonJS({
|
|
9
|
-
"bin/Koffi/package/src/cnoke/src/tools.js"(exports2, module2) {
|
|
9
|
+
"../../../bin/Koffi/package/src/cnoke/src/tools.js"(exports2, module2) {
|
|
10
10
|
"use strict";
|
|
11
11
|
var crypto = require("crypto");
|
|
12
12
|
var fs2 = require("fs");
|
|
@@ -397,12 +397,12 @@ var require_tools = __commonJS({
|
|
|
397
397
|
}
|
|
398
398
|
});
|
|
399
399
|
|
|
400
|
-
// bin/Koffi/package/src/koffi/package.json
|
|
400
|
+
// ../../../bin/Koffi/package/src/koffi/package.json
|
|
401
401
|
var require_package = __commonJS({
|
|
402
|
-
"bin/Koffi/package/src/koffi/package.json"(exports2, module2) {
|
|
402
|
+
"../../../bin/Koffi/package/src/koffi/package.json"(exports2, module2) {
|
|
403
403
|
module2.exports = {
|
|
404
404
|
name: "koffi",
|
|
405
|
-
version: "2.14.0
|
|
405
|
+
version: "2.14.0",
|
|
406
406
|
description: "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
407
407
|
keywords: [
|
|
408
408
|
"foreign",
|
|
@@ -443,9 +443,9 @@ var require_package = __commonJS({
|
|
|
443
443
|
}
|
|
444
444
|
});
|
|
445
445
|
|
|
446
|
-
// bin/Koffi/package/src/koffi/src/init.js
|
|
446
|
+
// ../../../bin/Koffi/package/src/koffi/src/init.js
|
|
447
447
|
var require_init = __commonJS({
|
|
448
|
-
"bin/Koffi/package/src/koffi/src/init.js"(exports, module) {
|
|
448
|
+
"../../../bin/Koffi/package/src/koffi/src/init.js"(exports, module) {
|
|
449
449
|
var fs = require("fs");
|
|
450
450
|
var path = require("path");
|
|
451
451
|
var util = require("util");
|
|
@@ -528,7 +528,7 @@ var require_init = __commonJS({
|
|
|
528
528
|
}
|
|
529
529
|
});
|
|
530
530
|
|
|
531
|
-
// bin/Koffi/package/src/koffi/indirect.js
|
|
531
|
+
// ../../../bin/Koffi/package/src/koffi/indirect.js
|
|
532
532
|
var { detect: detect2, init: init2 } = require_init();
|
|
533
533
|
var triplet2 = detect2();
|
|
534
534
|
var mod2 = init2(triplet2, null);
|
package/package.json
CHANGED
package/src/core/base/base.cc
CHANGED
|
@@ -854,6 +854,31 @@ TimeSpec DecomposeTimeLocal(int64_t time)
|
|
|
854
854
|
return spec;
|
|
855
855
|
}
|
|
856
856
|
|
|
857
|
+
int64_t ComposeTimeUTC(const TimeSpec &spec)
|
|
858
|
+
{
|
|
859
|
+
RG_ASSERT(!spec.offset);
|
|
860
|
+
|
|
861
|
+
struct tm ti = {};
|
|
862
|
+
|
|
863
|
+
ti.tm_year = spec.year - 1900;
|
|
864
|
+
ti.tm_mon = spec.month - 1;
|
|
865
|
+
ti.tm_mday = spec.day;
|
|
866
|
+
ti.tm_hour = spec.hour;
|
|
867
|
+
ti.tm_min = spec.min;
|
|
868
|
+
ti.tm_sec = spec.sec;
|
|
869
|
+
|
|
870
|
+
#if defined(_WIN32)
|
|
871
|
+
int64_t time = (int64_t)_mkgmtime64(&ti);
|
|
872
|
+
#else
|
|
873
|
+
int64_t time = (int64_t)timegm(&ti);
|
|
874
|
+
#endif
|
|
875
|
+
|
|
876
|
+
time *= 1000;
|
|
877
|
+
time += spec.msec;
|
|
878
|
+
|
|
879
|
+
return time;
|
|
880
|
+
}
|
|
881
|
+
|
|
857
882
|
// ------------------------------------------------------------------------
|
|
858
883
|
// Strings
|
|
859
884
|
// ------------------------------------------------------------------------
|
package/src/core/base/base.hh
CHANGED
|
@@ -3669,6 +3669,7 @@ struct TimeSpec {
|
|
|
3669
3669
|
|
|
3670
3670
|
TimeSpec DecomposeTimeUTC(int64_t time);
|
|
3671
3671
|
TimeSpec DecomposeTimeLocal(int64_t time);
|
|
3672
|
+
int64_t ComposeTimeUTC(const TimeSpec &spec);
|
|
3672
3673
|
|
|
3673
3674
|
// ------------------------------------------------------------------------
|
|
3674
3675
|
// Format
|
package/src/koffi/src/call.cc
CHANGED
|
@@ -517,7 +517,7 @@ bool CallData::PushObject(Napi::Object obj, const TypeInfo *type, uint8_t *origi
|
|
|
517
517
|
RG_UNREACHABLE();
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
-
|
|
520
|
+
MemSet(origin, 0, type->size);
|
|
521
521
|
|
|
522
522
|
for (Size i = 0; i < members.len; i++) {
|
|
523
523
|
const RecordMember &member = members[i];
|
package/src/koffi/src/call.hh
CHANGED
|
@@ -156,7 +156,7 @@ inline bool CallData::AllocStack(Size size, Size align, T **out_ptr)
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
#if defined(RG_DEBUG)
|
|
159
|
-
|
|
159
|
+
MemSet(ptr, 0, delta);
|
|
160
160
|
#endif
|
|
161
161
|
|
|
162
162
|
mem->stack.len -= delta;
|
|
@@ -173,7 +173,7 @@ inline T *CallData::AllocHeap(Size size, Size align)
|
|
|
173
173
|
|
|
174
174
|
if (size < 4096 && delta <= mem->heap.len) [[likely]] {
|
|
175
175
|
#if defined(RG_DEBUG)
|
|
176
|
-
|
|
176
|
+
MemSet(mem->heap.ptr, 0, delta);
|
|
177
177
|
#endif
|
|
178
178
|
|
|
179
179
|
mem->heap.ptr += delta;
|
package/src/koffi/src/ffi.cc
CHANGED
|
@@ -986,7 +986,11 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
986
986
|
return env.Null();
|
|
987
987
|
}
|
|
988
988
|
if (!info[1 + dynamic].IsNumber()) {
|
|
989
|
-
|
|
989
|
+
if (info.Length() == 2 && info[1].IsString()) {
|
|
990
|
+
ThrowError<Napi::TypeError>(env, "Missing maxLen argument");
|
|
991
|
+
} else {
|
|
992
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for %2, expected integer", GetValueType(instance, info[1]), dynamic ? "maxLen" : "len");
|
|
993
|
+
}
|
|
990
994
|
return env.Null();
|
|
991
995
|
}
|
|
992
996
|
|
|
@@ -1003,7 +1007,7 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
1003
1007
|
|
|
1004
1008
|
TypeInfo *type = nullptr;
|
|
1005
1009
|
|
|
1006
|
-
if (info.Length() >=
|
|
1010
|
+
if (info.Length() >= 3u + dynamic && !IsNullOrUndefined(info[2 + dynamic])) {
|
|
1007
1011
|
if (!info[2 + dynamic].IsString()) {
|
|
1008
1012
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for hint, expected string", GetValueType(instance, info[2]));
|
|
1009
1013
|
return env.Null();
|
|
@@ -2087,7 +2091,7 @@ static Napi::Value DecodeValue(const Napi::CallbackInfo &info)
|
|
|
2087
2091
|
Napi::Env env = info.Env();
|
|
2088
2092
|
|
|
2089
2093
|
bool has_offset = (info.Length() >= 2 && info[1].IsNumber());
|
|
2090
|
-
bool has_len = (info.Length() >= 3u + has_offset && info[
|
|
2094
|
+
bool has_len = (info.Length() >= 3u + has_offset && info[2 + has_offset].IsNumber());
|
|
2091
2095
|
|
|
2092
2096
|
if (info.Length() < 2u + has_offset) [[unlikely]] {
|
|
2093
2097
|
ThrowError<Napi::TypeError>(env, "Expected %1 to 4 arguments, got %2", 2 + has_offset, info.Length());
|
|
@@ -2102,7 +2106,7 @@ static Napi::Value DecodeValue(const Napi::CallbackInfo &info)
|
|
|
2102
2106
|
int64_t offset = has_offset ? info[1].As<Napi::Number>().Int64Value() : 0;
|
|
2103
2107
|
|
|
2104
2108
|
if (has_len) {
|
|
2105
|
-
Size len = info[
|
|
2109
|
+
Size len = info[2 + has_offset].As<Napi::Number>();
|
|
2106
2110
|
|
|
2107
2111
|
Napi::Value ret = Decode(value, offset, type, &len);
|
|
2108
2112
|
return ret;
|
|
@@ -2164,7 +2168,7 @@ static Napi::Value EncodeValue(const Napi::CallbackInfo &info)
|
|
|
2164
2168
|
Napi::Env env = info.Env();
|
|
2165
2169
|
|
|
2166
2170
|
bool has_offset = (info.Length() >= 2 && info[1].IsNumber());
|
|
2167
|
-
bool has_len = (info.Length() >= 4u + has_offset && info[
|
|
2171
|
+
bool has_len = (info.Length() >= 4u + has_offset && info[3 + has_offset].IsNumber());
|
|
2168
2172
|
|
|
2169
2173
|
if (info.Length() < 3u + has_offset) [[unlikely]] {
|
|
2170
2174
|
ThrowError<Napi::TypeError>(env, "Expected %1 to 5 arguments, got %2", 3 + has_offset, info.Length());
|
|
@@ -2177,10 +2181,10 @@ static Napi::Value EncodeValue(const Napi::CallbackInfo &info)
|
|
|
2177
2181
|
|
|
2178
2182
|
Napi::Value ref = info[0];
|
|
2179
2183
|
int64_t offset = has_offset ? info[1].As<Napi::Number>().Int64Value() : 0;
|
|
2180
|
-
Napi::Value value = info[
|
|
2184
|
+
Napi::Value value = info[2 + has_offset];
|
|
2181
2185
|
|
|
2182
2186
|
if (has_len) {
|
|
2183
|
-
Size len = info[
|
|
2187
|
+
Size len = info[3 + has_offset].As<Napi::Number>();
|
|
2184
2188
|
|
|
2185
2189
|
if (!Encode(ref, offset, value, type, &len))
|
|
2186
2190
|
return env.Null();
|