koffi 2.14.0-beta.2 → 2.14.0-beta.3
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/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/benchmarks.md +11 -11
- package/doc/static/perf_linux.png +0 -0
- package/doc/static/perf_windows.png +0 -0
- package/index.d.ts +3 -2
- package/index.js +8 -8
- package/indirect.js +8 -8
- package/package.json +1 -1
- package/src/koffi/src/call.cc +29 -26
- package/src/koffi/src/call.hh +2 -2
- package/src/koffi/src/ffi.cc +30 -30
- package/src/koffi/src/util.cc +7 -4
|
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/benchmarks.md
CHANGED
|
@@ -15,7 +15,7 @@ These results are detailed and explained below, and compared to node-ffi/node-ff
|
|
|
15
15
|
|
|
16
16
|
# Linux x86_64
|
|
17
17
|
|
|
18
|
-
The results presented below were measured on my x86_64 Linux machine (
|
|
18
|
+
The results presented below were measured on my x86_64 Linux machine (AMD Ryzen™ 5 2600).
|
|
19
19
|
|
|
20
20
|
## rand results
|
|
21
21
|
|
|
@@ -27,9 +27,9 @@ This test is based around repeated calls to a simple standard C function `rand`,
|
|
|
27
27
|
|
|
28
28
|
Benchmark | Iteration time | Relative performance | Overhead
|
|
29
29
|
------------- | -------------- | -------------------- | --------
|
|
30
|
-
rand_napi |
|
|
31
|
-
rand_koffi |
|
|
32
|
-
rand_node_ffi |
|
|
30
|
+
rand_napi | 569 ns | x1.00 | (ref)
|
|
31
|
+
rand_koffi | 855 ns | x0.67 | +50%
|
|
32
|
+
rand_node_ffi | 58730 ns | x0.010 | +10228%
|
|
33
33
|
|
|
34
34
|
Because rand is a pretty small function, the FFI overhead is clearly visible.
|
|
35
35
|
|
|
@@ -39,9 +39,9 @@ This test is similar to the rand one, but it is based on `atoi`, which takes a s
|
|
|
39
39
|
|
|
40
40
|
Benchmark | Iteration time | Relative performance | Overhead
|
|
41
41
|
------------- | -------------- | -------------------- | --------
|
|
42
|
-
atoi_napi |
|
|
43
|
-
atoi_koffi |
|
|
44
|
-
atoi_node_ffi |
|
|
42
|
+
atoi_napi | 1039 ns | x1.00 | (ref)
|
|
43
|
+
atoi_koffi | 1642 ns | x0.63 | +58%
|
|
44
|
+
atoi_node_ffi | 164790 ns | x0.006 | +15767%
|
|
45
45
|
|
|
46
46
|
Because atoi is a pretty small function, the FFI overhead is clearly visible.
|
|
47
47
|
|
|
@@ -54,10 +54,10 @@ This benchmark uses the CPU-based image drawing functions in Raylib. The calls a
|
|
|
54
54
|
|
|
55
55
|
Benchmark | Iteration time | Relative performance | Overhead
|
|
56
56
|
------------------ | -------------- | -------------------- | --------
|
|
57
|
-
raylib_cc |
|
|
58
|
-
raylib_node_raylib |
|
|
59
|
-
raylib_koffi | 28.
|
|
60
|
-
raylib_node_ffi |
|
|
57
|
+
raylib_cc | 17.5 µs | x1.34 | -25%
|
|
58
|
+
raylib_node_raylib | 23.4 µs | x1.00 | (ref)
|
|
59
|
+
raylib_koffi | 28.8 µs | x0.81 | +23%
|
|
60
|
+
raylib_node_ffi | 103.9 µs | x0.23 | +344%
|
|
61
61
|
|
|
62
62
|
# Windows x86_64
|
|
63
63
|
|
|
Binary file
|
|
Binary file
|
package/index.d.ts
CHANGED
|
@@ -105,14 +105,15 @@ 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
|
|
108
|
+
export function array(ref: TypeSpec, countedBy: string, hint?: ArrayHint | null): IKoffiCType;
|
|
109
|
+
export function array(ref: TypeSpec, countedBy: string, maxLen: number, hint?: ArrayHint | null): IKoffiCType;
|
|
109
110
|
|
|
110
111
|
export function opaque(name: string | null | undefined): IKoffiCType;
|
|
111
112
|
export function opaque(): IKoffiCType;
|
|
112
113
|
/** @deprecated */ export function handle(name: string | null | undefined): IKoffiCType;
|
|
113
114
|
/** @deprecated */ export function handle(): IKoffiCType;
|
|
114
115
|
|
|
115
|
-
export function pointer(ref: TypeSpec
|
|
116
|
+
export function pointer(ref: TypeSpec): IKoffiCType;
|
|
116
117
|
export function pointer(ref: TypeSpec, count: number): IKoffiCType;
|
|
117
118
|
export function pointer(name: string | null | undefined, ref: TypeSpec, countedBy?: string | null): IKoffiCType;
|
|
118
119
|
export function pointer(name: string | null | undefined, ref: TypeSpec, count: number): 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
|
-
//
|
|
7
|
+
// bin/Koffi/package/src/cnoke/src/tools.js
|
|
8
8
|
var require_tools = __commonJS({
|
|
9
|
-
"
|
|
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
|
-
//
|
|
400
|
+
// bin/Koffi/package/src/koffi/package.json
|
|
401
401
|
var require_package = __commonJS({
|
|
402
|
-
"
|
|
402
|
+
"bin/Koffi/package/src/koffi/package.json"(exports2, module2) {
|
|
403
403
|
module2.exports = {
|
|
404
404
|
name: "koffi",
|
|
405
|
-
version: "2.14.0-beta.
|
|
405
|
+
version: "2.14.0-beta.3",
|
|
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
|
-
//
|
|
446
|
+
// bin/Koffi/package/src/koffi/src/init.js
|
|
447
447
|
var require_init = __commonJS({
|
|
448
|
-
"
|
|
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
|
-
//
|
|
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
|
-
//
|
|
7
|
+
// bin/Koffi/package/src/cnoke/src/tools.js
|
|
8
8
|
var require_tools = __commonJS({
|
|
9
|
-
"
|
|
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
|
-
//
|
|
400
|
+
// bin/Koffi/package/src/koffi/package.json
|
|
401
401
|
var require_package = __commonJS({
|
|
402
|
-
"
|
|
402
|
+
"bin/Koffi/package/src/koffi/package.json"(exports2, module2) {
|
|
403
403
|
module2.exports = {
|
|
404
404
|
name: "koffi",
|
|
405
|
-
version: "2.14.0-beta.
|
|
405
|
+
version: "2.14.0-beta.3",
|
|
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
|
-
//
|
|
446
|
+
// bin/Koffi/package/src/koffi/src/init.js
|
|
447
447
|
var require_init = __commonJS({
|
|
448
|
-
"
|
|
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
|
-
//
|
|
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/koffi/src/call.cc
CHANGED
|
@@ -715,13 +715,11 @@ bool CallData::PushObject(Napi::Object obj, const TypeInfo *type, uint8_t *origi
|
|
|
715
715
|
case PrimitiveKind::Array: {
|
|
716
716
|
if (value.IsArray()) {
|
|
717
717
|
Napi::Array array = value.As<Napi::Array>();
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
if (!PushNormalArray(array, len, member.type, dest))
|
|
718
|
+
if (!PushNormalArray(array, member.type, member.type->size, dest))
|
|
721
719
|
return false;
|
|
722
720
|
} else if (IsRawBuffer(value)) {
|
|
723
721
|
Span<const uint8_t> buffer = GetRawBuffer(value);
|
|
724
|
-
PushBuffer(buffer, member.type
|
|
722
|
+
PushBuffer(buffer, member.type, dest);
|
|
725
723
|
} else if (value.IsString()) {
|
|
726
724
|
if (!PushStringArray(value, member.type, dest))
|
|
727
725
|
return false;
|
|
@@ -763,15 +761,18 @@ bool CallData::PushObject(Napi::Object obj, const TypeInfo *type, uint8_t *origi
|
|
|
763
761
|
return true;
|
|
764
762
|
}
|
|
765
763
|
|
|
766
|
-
bool CallData::PushNormalArray(Napi::Array array,
|
|
764
|
+
bool CallData::PushNormalArray(Napi::Array array, const TypeInfo *type, Size size, uint8_t *origin)
|
|
767
765
|
{
|
|
768
766
|
RG_ASSERT(array.IsArray());
|
|
769
767
|
|
|
770
768
|
const TypeInfo *ref = type->ref.type;
|
|
769
|
+
Size len = (Size)array.Length();
|
|
770
|
+
Size available = len * ref->size;
|
|
771
771
|
|
|
772
|
-
if (
|
|
773
|
-
|
|
774
|
-
|
|
772
|
+
if (available > size) {
|
|
773
|
+
len = size / ref->size;
|
|
774
|
+
} else {
|
|
775
|
+
MemSet(origin + available, 0, size - available);
|
|
775
776
|
}
|
|
776
777
|
|
|
777
778
|
Size offset = 0;
|
|
@@ -945,13 +946,11 @@ bool CallData::PushNormalArray(Napi::Array array, Size len, const TypeInfo *type
|
|
|
945
946
|
|
|
946
947
|
if (value.IsArray()) {
|
|
947
948
|
Napi::Array array2 = value.As<Napi::Array>();
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
if (!PushNormalArray(array2, len2, ref, dest))
|
|
949
|
+
if (!PushNormalArray(array2, ref, (Size)ref->size, dest))
|
|
951
950
|
return false;
|
|
952
951
|
} else if (IsRawBuffer(value)) {
|
|
953
952
|
Span<const uint8_t> buffer = GetRawBuffer(value);
|
|
954
|
-
PushBuffer(buffer, ref
|
|
953
|
+
PushBuffer(buffer, ref, dest);
|
|
955
954
|
} else if (value.IsString()) {
|
|
956
955
|
if (!PushStringArray(value, ref, dest))
|
|
957
956
|
return false;
|
|
@@ -1001,13 +1000,13 @@ bool CallData::PushNormalArray(Napi::Array array, Size len, const TypeInfo *type
|
|
|
1001
1000
|
return true;
|
|
1002
1001
|
}
|
|
1003
1002
|
|
|
1004
|
-
void CallData::PushBuffer(Span<const uint8_t> buffer,
|
|
1003
|
+
void CallData::PushBuffer(Span<const uint8_t> buffer, const TypeInfo *type, uint8_t *origin)
|
|
1005
1004
|
{
|
|
1006
|
-
buffer.len = std::min(buffer.len, size);
|
|
1005
|
+
buffer.len = std::min(buffer.len, (Size)type->size);
|
|
1007
1006
|
|
|
1008
1007
|
// Go fast brrrrrrr :)
|
|
1009
|
-
MemCpy(origin, buffer.ptr,
|
|
1010
|
-
MemSet(origin + buffer.len, 0, (
|
|
1008
|
+
MemCpy(origin, buffer.ptr, buffer.len);
|
|
1009
|
+
MemSet(origin + buffer.len, 0, (Size)type->size - buffer.len);
|
|
1011
1010
|
|
|
1012
1011
|
#define SWAP(CType) \
|
|
1013
1012
|
do { \
|
|
@@ -1131,7 +1130,7 @@ bool CallData::PushPointer(Napi::Value value, const TypeInfo *type, int directio
|
|
|
1131
1130
|
ptr = AllocHeap(size, 16);
|
|
1132
1131
|
|
|
1133
1132
|
if (directions & 1) {
|
|
1134
|
-
if (!PushNormalArray(array,
|
|
1133
|
+
if (!PushNormalArray(array, type, size, ptr))
|
|
1135
1134
|
return false;
|
|
1136
1135
|
} else {
|
|
1137
1136
|
MemSet(ptr, 0, size);
|
|
@@ -1369,9 +1368,9 @@ void CallData::DumpForward(const FunctionInfo *func) const
|
|
|
1369
1368
|
bool CallData::CheckDynamicLength(Napi::Object obj, Size element, const char *countedby, Napi::Value value)
|
|
1370
1369
|
{
|
|
1371
1370
|
int64_t expected = -1;
|
|
1372
|
-
int64_t
|
|
1371
|
+
int64_t size = -1;
|
|
1373
1372
|
|
|
1374
|
-
// Get expected
|
|
1373
|
+
// Get expected size
|
|
1375
1374
|
{
|
|
1376
1375
|
Napi::Value by = obj.Get(countedby);
|
|
1377
1376
|
|
|
@@ -1380,24 +1379,28 @@ bool CallData::CheckDynamicLength(Napi::Object obj, Size element, const char *co
|
|
|
1380
1379
|
return false;
|
|
1381
1380
|
}
|
|
1382
1381
|
|
|
1383
|
-
|
|
1382
|
+
// If we get anywhere near overflow there are other problems to worry about.
|
|
1383
|
+
// So let's not worry about that.
|
|
1384
|
+
expected = GetNumber<int64_t>(by) * element;
|
|
1384
1385
|
}
|
|
1385
1386
|
|
|
1386
|
-
// Get actual
|
|
1387
|
+
// Get actual size
|
|
1387
1388
|
if (value.IsArray()) {
|
|
1388
1389
|
Napi::Array array = value.As<Napi::Array>();
|
|
1389
|
-
|
|
1390
|
+
size = array.Length() * element;
|
|
1390
1391
|
} else if (value.IsTypedArray()) {
|
|
1391
1392
|
Napi::TypedArray typed = value.As<Napi::TypedArray>();
|
|
1392
|
-
|
|
1393
|
+
size = typed.ByteLength();
|
|
1393
1394
|
} else if (value.IsArrayBuffer()) {
|
|
1394
1395
|
Napi::ArrayBuffer buffer = value.As<Napi::ArrayBuffer>();
|
|
1395
|
-
|
|
1396
|
+
size = buffer.ByteLength();
|
|
1397
|
+
} else if (!IsNullOrUndefined(value)) {
|
|
1398
|
+
size = element;
|
|
1396
1399
|
} else {
|
|
1397
|
-
|
|
1400
|
+
size = 0;
|
|
1398
1401
|
}
|
|
1399
1402
|
|
|
1400
|
-
if (
|
|
1403
|
+
if (size != expected) {
|
|
1401
1404
|
ThrowError<Napi::Error>(env, "Mismatched dynamic length between '%1' and actual array", countedby);
|
|
1402
1405
|
return false;
|
|
1403
1406
|
}
|
package/src/koffi/src/call.hh
CHANGED
|
@@ -121,8 +121,8 @@ public:
|
|
|
121
121
|
bool PushString32(Napi::Value value, int directions, const char32_t **out_str32);
|
|
122
122
|
Size PushString32Value(Napi::Value value, const char32_t **out_str32);
|
|
123
123
|
bool PushObject(Napi::Object obj, const TypeInfo *type, uint8_t *origin);
|
|
124
|
-
bool PushNormalArray(Napi::Array array,
|
|
125
|
-
void PushBuffer(Span<const uint8_t> buffer,
|
|
124
|
+
bool PushNormalArray(Napi::Array array, const TypeInfo *type, Size size, uint8_t *origin);
|
|
125
|
+
void PushBuffer(Span<const uint8_t> buffer, const TypeInfo *type, uint8_t *origin);
|
|
126
126
|
bool PushStringArray(Napi::Value value, const TypeInfo *type, uint8_t *origin);
|
|
127
127
|
bool PushPointer(Napi::Value value, const TypeInfo *type, int directions, void **out_ptr);
|
|
128
128
|
bool PushCallback(Napi::Value value, const TypeInfo *type, void **out_ptr);
|
package/src/koffi/src/ffi.cc
CHANGED
|
@@ -348,6 +348,8 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
|
|
|
348
348
|
size = member.offset + member.type->size;
|
|
349
349
|
type->align = std::max(type->align, align);
|
|
350
350
|
|
|
351
|
+
member.countedby = -1;
|
|
352
|
+
|
|
351
353
|
if (size > instance->config.max_type_size) {
|
|
352
354
|
ThrowError<Napi::Error>(env, "Struct '%1' size is too high (max = %2)", type->name, FmtMemSize(size));
|
|
353
355
|
return env.Null();
|
|
@@ -372,8 +374,9 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
|
|
|
372
374
|
type->members.Append(member);
|
|
373
375
|
}
|
|
374
376
|
|
|
375
|
-
for (
|
|
376
|
-
|
|
377
|
+
for (Size i = 0; i < type->members.len; i++) {
|
|
378
|
+
RecordMember *member = &type->members[i];
|
|
379
|
+
const char *countedby = member->type->countedby;
|
|
377
380
|
|
|
378
381
|
if (countedby) {
|
|
379
382
|
const RecordMember *by = std::find_if(type->members.begin(), type->members.end(),
|
|
@@ -387,10 +390,12 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
|
|
|
387
390
|
ThrowError<Napi::Error>(env, "Dynamic length member %1 is not an integer", countedby);
|
|
388
391
|
return env.Null();
|
|
389
392
|
}
|
|
393
|
+
if (member->type->primitive == PrimitiveKind::Array && i < type->members.len - 1) {
|
|
394
|
+
ThrowError<Napi::Error>(env, "Flexible array '%1' is not the last member of struct", member->name);
|
|
395
|
+
return env.Null();
|
|
396
|
+
}
|
|
390
397
|
|
|
391
|
-
member
|
|
392
|
-
} else {
|
|
393
|
-
member.countedby = -1;
|
|
398
|
+
member->countedby = by - type->members.ptr;
|
|
394
399
|
}
|
|
395
400
|
}
|
|
396
401
|
|
|
@@ -966,7 +971,7 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
966
971
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
967
972
|
|
|
968
973
|
if (info.Length() < 2) {
|
|
969
|
-
ThrowError<Napi::TypeError>(env, "Expected 2 to
|
|
974
|
+
ThrowError<Napi::TypeError>(env, "Expected 2 to 4 arguments, got %1", info.Length());
|
|
970
975
|
return env.Null();
|
|
971
976
|
}
|
|
972
977
|
|
|
@@ -974,27 +979,22 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
974
979
|
if (!ref)
|
|
975
980
|
return env.Null();
|
|
976
981
|
|
|
977
|
-
|
|
978
|
-
Napi::Value countedby;
|
|
979
|
-
|
|
980
|
-
if (info[1].IsNumber()) {
|
|
981
|
-
len = info[1].As<Napi::Number>().Int64Value();
|
|
982
|
-
} else if (info[1].IsString()) {
|
|
983
|
-
countedby = info[1];
|
|
982
|
+
bool dynamic = (info.Length() >= 3) && info[1].IsString();
|
|
984
983
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
} else {
|
|
989
|
-
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for length, expected integer or string", GetValueType(instance, info[1]));
|
|
984
|
+
if (dynamic && !info[1].IsString()) {
|
|
985
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for countedBy, expected string", GetValueType(instance, info[1]));
|
|
986
|
+
return env.Null();
|
|
990
987
|
}
|
|
988
|
+
if (!info[1 + dynamic].IsNumber()) {
|
|
989
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for %2, expected integer", GetValueType(instance, info[1]), dynamic ? "maxLen" : "len");
|
|
990
|
+
return env.Null();
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
int64_t len = info[1 + dynamic].As<Napi::Number>().Int64Value();
|
|
991
994
|
|
|
992
|
-
if (
|
|
995
|
+
if (len <= 0) {
|
|
993
996
|
ThrowError<Napi::TypeError>(env, "Array length must be positive and non-zero");
|
|
994
997
|
return env.Null();
|
|
995
|
-
} else if (len < 0) {
|
|
996
|
-
ThrowError<Napi::TypeError>(env, "Array length must be positive");
|
|
997
|
-
return env.Null();
|
|
998
998
|
}
|
|
999
999
|
if (len > instance->config.max_type_size / ref->size) {
|
|
1000
1000
|
ThrowError<Napi::TypeError>(env, "Array length is too high (max = %1)", instance->config.max_type_size / ref->size);
|
|
@@ -1003,25 +1003,25 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
1003
1003
|
|
|
1004
1004
|
TypeInfo *type = nullptr;
|
|
1005
1005
|
|
|
1006
|
-
if (info.Length() >= 3 && !IsNullOrUndefined(info[2])) {
|
|
1007
|
-
if (!info[2].IsString()) {
|
|
1006
|
+
if (info.Length() >= 3 + dynamic && !IsNullOrUndefined(info[2 + dynamic])) {
|
|
1007
|
+
if (!info[2 + dynamic].IsString()) {
|
|
1008
1008
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for hint, expected string", GetValueType(instance, info[2]));
|
|
1009
1009
|
return env.Null();
|
|
1010
1010
|
}
|
|
1011
1011
|
|
|
1012
|
-
std::string
|
|
1012
|
+
std::string str = info[2 + dynamic].As<Napi::String>();
|
|
1013
1013
|
ArrayHint hint = {};
|
|
1014
1014
|
|
|
1015
|
-
if (
|
|
1015
|
+
if (str == "Typed" || str == "typed") {
|
|
1016
1016
|
if (!(ref->flags & (int)TypeFlag::HasTypedArray)) {
|
|
1017
1017
|
ThrowError<Napi::Error>(env, "Array hint 'Typed' cannot be used with type %1", ref->name);
|
|
1018
1018
|
return env.Null();
|
|
1019
1019
|
}
|
|
1020
1020
|
|
|
1021
1021
|
hint = ArrayHint::Typed;
|
|
1022
|
-
} else if (
|
|
1022
|
+
} else if (str == "Array" || str == "array") {
|
|
1023
1023
|
hint = ArrayHint::Array;
|
|
1024
|
-
} else if (
|
|
1024
|
+
} else if (str == "String" || str == "string") {
|
|
1025
1025
|
if (ref->primitive != PrimitiveKind::Int8 && ref->primitive != PrimitiveKind::Int16) {
|
|
1026
1026
|
ThrowError<Napi::Error>(env, "Array hint 'String' can only be used with 8 and 16-bit signed integer types");
|
|
1027
1027
|
return env.Null();
|
|
@@ -1038,8 +1038,8 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
1038
1038
|
type = MakeArrayType(instance, ref, len);
|
|
1039
1039
|
}
|
|
1040
1040
|
|
|
1041
|
-
if (
|
|
1042
|
-
Napi::String str =
|
|
1041
|
+
if (dynamic) {
|
|
1042
|
+
Napi::String str = info[1].As<Napi::String>();
|
|
1043
1043
|
type->countedby = DuplicateString(str.Utf8Value().c_str(), &instance->str_alloc).ptr;
|
|
1044
1044
|
}
|
|
1045
1045
|
|
package/src/koffi/src/util.cc
CHANGED
|
@@ -906,7 +906,12 @@ void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type)
|
|
|
906
906
|
case PrimitiveKind::Array: {
|
|
907
907
|
if (member.countedby >= 0) {
|
|
908
908
|
const RecordMember &by = type->members[member.countedby];
|
|
909
|
+
|
|
909
910
|
uint32_t len = DecodeDynamicLength(origin, by);
|
|
911
|
+
uint32_t max = member.type->size / member.type->ref.type->size;
|
|
912
|
+
|
|
913
|
+
// Silently truncate result
|
|
914
|
+
len = std::min(len, max);
|
|
910
915
|
|
|
911
916
|
Napi::Value value = DecodeArray(env, src, member.type, len);
|
|
912
917
|
obj.Set(member.name, value);
|
|
@@ -1634,13 +1639,11 @@ bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *t
|
|
|
1634
1639
|
case PrimitiveKind::Array: {
|
|
1635
1640
|
if (value.IsArray()) {
|
|
1636
1641
|
Napi::Array array = value.As<Napi::Array>();
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
if (!call.PushNormalArray(array, len, type, origin))
|
|
1642
|
+
if (!call.PushNormalArray(array, type, type->size, origin))
|
|
1640
1643
|
return false;
|
|
1641
1644
|
} else if (IsRawBuffer(value)) {
|
|
1642
1645
|
Span<const uint8_t> buffer = GetRawBuffer(value);
|
|
1643
|
-
call.PushBuffer(buffer, type
|
|
1646
|
+
call.PushBuffer(buffer, type, origin);
|
|
1644
1647
|
} else if (value.IsString()) {
|
|
1645
1648
|
if (!call.PushStringArray(value, type, origin))
|
|
1646
1649
|
return false;
|