koffi 2.13.0 → 2.14.0-beta.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/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/index.d.ts +39 -14
- package/index.js +8 -8
- package/indirect.js +8 -8
- package/package.json +1 -1
- package/src/koffi/src/ffi.cc +87 -26
- package/src/koffi/src/ffi.hh +2 -0
- package/src/koffi/src/parser.cc +12 -1
- package/src/koffi/src/util.cc +173 -68
- package/src/koffi/src/util.hh +6 -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/index.d.ts
CHANGED
|
@@ -19,7 +19,13 @@
|
|
|
19
19
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
20
20
|
// OTHER DEALINGS IN THE SOFTWARE.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
type LoadOptions = {
|
|
23
|
+
lazy?: boolean,
|
|
24
|
+
global?: boolean,
|
|
25
|
+
deep?: boolean
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function load(path: string | null, options?: LoadOptions): IKoffiLib;
|
|
23
29
|
|
|
24
30
|
interface IKoffiCType { __brand: 'IKoffiCType' }
|
|
25
31
|
interface IKoffiPointerCast { __brand: 'IKoffiPointerCast' }
|
|
@@ -65,17 +71,17 @@ export type KoffiFunc<T extends (...args: any) => any> = T & {
|
|
|
65
71
|
|
|
66
72
|
export interface IKoffiLib {
|
|
67
73
|
func(definition: string): KoffiFunction;
|
|
68
|
-
func(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
69
|
-
func(convention: string, name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
74
|
+
func(name: string | number, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
75
|
+
func(convention: string, name: string | number, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
70
76
|
|
|
71
77
|
/** @deprecated */ cdecl(definition: string): KoffiFunction;
|
|
72
|
-
/** @deprecated */ cdecl(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
78
|
+
/** @deprecated */ cdecl(name: string | number, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
73
79
|
/** @deprecated */ stdcall(definition: string): KoffiFunction;
|
|
74
|
-
/** @deprecated */ stdcall(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
80
|
+
/** @deprecated */ stdcall(name: string | number, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
75
81
|
/** @deprecated */ fastcall(definition: string): KoffiFunction;
|
|
76
|
-
/** @deprecated */ fastcall(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
82
|
+
/** @deprecated */ fastcall(name: string | number, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
77
83
|
/** @deprecated */ thiscall(definition: string): KoffiFunction;
|
|
78
|
-
/** @deprecated */ thiscall(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
84
|
+
/** @deprecated */ thiscall(name: string | number, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
|
|
79
85
|
|
|
80
86
|
symbol(name: string, type: TypeSpec): any;
|
|
81
87
|
|
|
@@ -99,15 +105,16 @@ export class Union {
|
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
export function array(ref: TypeSpec, len: number, hint?: ArrayHint | null): IKoffiCType;
|
|
108
|
+
export function array(ref: TypeSpec, countedBy: string, hint?: ArrayHint | null, len?: number | null): IKoffiCType;
|
|
102
109
|
|
|
103
110
|
export function opaque(name: string | null | undefined): IKoffiCType;
|
|
104
111
|
export function opaque(): IKoffiCType;
|
|
105
112
|
/** @deprecated */ export function handle(name: string | null | undefined): IKoffiCType;
|
|
106
113
|
/** @deprecated */ export function handle(): IKoffiCType;
|
|
107
114
|
|
|
108
|
-
export function pointer(ref: TypeSpec): IKoffiCType;
|
|
115
|
+
export function pointer(ref: TypeSpec, countedBy?: string | null): IKoffiCType;
|
|
109
116
|
export function pointer(ref: TypeSpec, count: number): IKoffiCType;
|
|
110
|
-
export function pointer(name: string | null | undefined, ref: TypeSpec): IKoffiCType;
|
|
117
|
+
export function pointer(name: string | null | undefined, ref: TypeSpec, countedBy?: string | null): IKoffiCType;
|
|
111
118
|
export function pointer(name: string | null | undefined, ref: TypeSpec, count: number): IKoffiCType;
|
|
112
119
|
|
|
113
120
|
export function out(type: TypeSpec): IKoffiCType;
|
|
@@ -155,9 +162,22 @@ export function introspect(type: TypeSpec): TypeInfo;
|
|
|
155
162
|
|
|
156
163
|
export function alias(name: string, type: TypeSpec): IKoffiCType;
|
|
157
164
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
type KoffiConfig = {
|
|
166
|
+
sync_stack_size: number
|
|
167
|
+
sync_heap_size: number
|
|
168
|
+
async_stack_size: number
|
|
169
|
+
async_heap_size: number
|
|
170
|
+
resident_async_pools: number
|
|
171
|
+
max_async_calls: number
|
|
172
|
+
max_type_size: number
|
|
173
|
+
};
|
|
174
|
+
type KoffiStats = {
|
|
175
|
+
disposed: number
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export function config(): KoffiConfig;
|
|
179
|
+
export function config(cfg: KoffiConfig): KoffiConfig;
|
|
180
|
+
export function stats(): KoffiStats;
|
|
161
181
|
|
|
162
182
|
export function alloc(type: TypeSpec, length: number): any;
|
|
163
183
|
export function free(value: any): void;
|
|
@@ -254,5 +274,10 @@ type PrimitiveTypes =
|
|
|
254
274
|
| 'ushort'
|
|
255
275
|
| 'void'
|
|
256
276
|
| 'wchar'
|
|
257
|
-
| 'wchar_t'
|
|
258
|
-
export const types: Record<PrimitiveTypes, IKoffiCType
|
|
277
|
+
| 'wchar_t';
|
|
278
|
+
export const types: Record<PrimitiveTypes, IKoffiCType>;
|
|
279
|
+
|
|
280
|
+
// Internal stuff, don't use!
|
|
281
|
+
export const node: {
|
|
282
|
+
env: { __brand: 'IKoffiNodeEnv' }
|
|
283
|
+
};
|
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.
|
|
405
|
+
version: "2.14.0-beta.1",
|
|
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.
|
|
405
|
+
version: "2.14.0-beta.1",
|
|
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/ffi.cc
CHANGED
|
@@ -232,6 +232,33 @@ static bool MapType(Napi::Env env, InstanceData *instance, const TypeInfo *type,
|
|
|
232
232
|
return true;
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
static bool CheckDynamicMembers(Napi::Env env, TypeInfo *type)
|
|
236
|
+
{
|
|
237
|
+
for (RecordMember &member: type->members) {
|
|
238
|
+
const char *countedby = member.type->countedby;
|
|
239
|
+
|
|
240
|
+
if (countedby) {
|
|
241
|
+
const RecordMember *by = std::find_if(type->members.begin(), type->members.end(),
|
|
242
|
+
[&](const RecordMember &member) { return TestStr(member.name, countedby); });
|
|
243
|
+
|
|
244
|
+
if (by == member.type->members.end()) {
|
|
245
|
+
ThrowError<Napi::Error>(env, "Record type %1 does not have member '%2'", type->name, countedby);
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
if (!IsInteger(by->type)) {
|
|
249
|
+
ThrowError<Napi::Error>(env, "Dynamic length member %1 is not an integer", countedby);
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
member.countedby = by - type->members.ptr;
|
|
254
|
+
} else {
|
|
255
|
+
member.countedby = -1;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
|
|
235
262
|
static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
|
|
236
263
|
{
|
|
237
264
|
Napi::Env env = info.Env();
|
|
@@ -372,6 +399,9 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
|
|
|
372
399
|
type->members.Append(member);
|
|
373
400
|
}
|
|
374
401
|
|
|
402
|
+
if (!CheckDynamicMembers(env, type))
|
|
403
|
+
return env.Null();
|
|
404
|
+
|
|
375
405
|
size = (int32_t)AlignLen(size, type->align);
|
|
376
406
|
if (!size) {
|
|
377
407
|
ThrowError<Napi::Error>(env, "Empty struct '%1' is not allowed in C", type->name);
|
|
@@ -531,6 +561,9 @@ static Napi::Value CreateUnionType(const Napi::CallbackInfo &info)
|
|
|
531
561
|
type->members.Append(member);
|
|
532
562
|
}
|
|
533
563
|
|
|
564
|
+
if (!CheckDynamicMembers(env, type))
|
|
565
|
+
return env.Null();
|
|
566
|
+
|
|
534
567
|
size = (int32_t)AlignLen(size, type->align);
|
|
535
568
|
if (!size) {
|
|
536
569
|
ThrowError<Napi::Error>(env, "Empty union '%1' is not allowed in C", type->name);
|
|
@@ -633,39 +666,46 @@ static Napi::Value CreatePointerType(const Napi::CallbackInfo &info)
|
|
|
633
666
|
|
|
634
667
|
std::string name = named ? info[0].As<Napi::String>() : std::string();
|
|
635
668
|
|
|
636
|
-
const TypeInfo *
|
|
637
|
-
if (!
|
|
669
|
+
const TypeInfo *ref = ResolveType(info[skip]);
|
|
670
|
+
if (!ref)
|
|
638
671
|
return env.Null();
|
|
639
672
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
if (!info[1 + skip].IsNumber()) {
|
|
643
|
-
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for count, expected number", GetValueType(instance, info[1 + skip]));
|
|
644
|
-
return env.Null();
|
|
645
|
-
}
|
|
673
|
+
Napi::Value countedby;
|
|
674
|
+
int count = 1;
|
|
646
675
|
|
|
647
|
-
|
|
676
|
+
if (info.Length() >= 2u + skip) {
|
|
677
|
+
if (info[1 + skip].IsString()) {
|
|
678
|
+
countedby = info[1 + skip];
|
|
679
|
+
} else if (info[1 + skip].IsNumber()) {
|
|
680
|
+
count = info[1 + skip].As<Napi::Number>();
|
|
648
681
|
|
|
649
|
-
|
|
650
|
-
|
|
682
|
+
if (count < 1 || count > 4) {
|
|
683
|
+
ThrowError<Napi::TypeError>(env, "Value of count must be between 1 and 4");
|
|
684
|
+
return env.Null();
|
|
685
|
+
}
|
|
686
|
+
} else {
|
|
687
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for count, expected number", GetValueType(instance, info[1 + skip]));
|
|
651
688
|
return env.Null();
|
|
652
689
|
}
|
|
653
|
-
} else {
|
|
654
|
-
count = 1;
|
|
655
690
|
}
|
|
656
691
|
|
|
657
|
-
type = MakePointerType(instance,
|
|
692
|
+
TypeInfo *type = MakePointerType(instance, ref, count);
|
|
658
693
|
RG_ASSERT(type);
|
|
659
694
|
|
|
660
|
-
if (named) {
|
|
695
|
+
if (named || !countedby.IsEmpty()) {
|
|
661
696
|
TypeInfo *copy = instance->types.AppendDefault();
|
|
662
697
|
RG_DEFER_N(err_guard) { instance->types.RemoveLast(1); };
|
|
663
698
|
|
|
664
699
|
memcpy((void *)copy, type, RG_SIZE(*type));
|
|
665
|
-
copy->name = DuplicateString(name.c_str(), &instance->str_alloc).ptr;
|
|
700
|
+
copy->name = named ? DuplicateString(name.c_str(), &instance->str_alloc).ptr : copy->name;
|
|
701
|
+
|
|
702
|
+
if (!countedby.IsEmpty()) {
|
|
703
|
+
Napi::String str = countedby.As<Napi::String>();
|
|
704
|
+
copy->countedby = DuplicateString(str.Utf8Value().c_str(), &instance->str_alloc).ptr;
|
|
705
|
+
}
|
|
666
706
|
|
|
667
707
|
// If the insert succeeds, we cannot fail anymore
|
|
668
|
-
if (!MapType(env, instance, copy, copy->name))
|
|
708
|
+
if (named && !MapType(env, instance, copy, copy->name))
|
|
669
709
|
return env.Null();
|
|
670
710
|
err_guard.Disable();
|
|
671
711
|
|
|
@@ -931,29 +971,42 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
931
971
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
932
972
|
|
|
933
973
|
if (info.Length() < 2) {
|
|
934
|
-
ThrowError<Napi::TypeError>(env, "Expected 2 arguments, got %1", info.Length());
|
|
935
|
-
return env.Null();
|
|
936
|
-
}
|
|
937
|
-
if (!info[1].IsNumber()) {
|
|
938
|
-
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for length, expected integer", GetValueType(instance, info[1]));
|
|
974
|
+
ThrowError<Napi::TypeError>(env, "Expected 2 to 3 arguments, got %1", info.Length());
|
|
939
975
|
return env.Null();
|
|
940
976
|
}
|
|
941
977
|
|
|
942
978
|
const TypeInfo *ref = ResolveType(info[0]);
|
|
943
|
-
int64_t len = info[1].As<Napi::Number>().Int64Value();
|
|
944
|
-
|
|
945
979
|
if (!ref)
|
|
946
980
|
return env.Null();
|
|
947
|
-
|
|
981
|
+
|
|
982
|
+
int64_t len = 0;
|
|
983
|
+
Napi::Value countedby;
|
|
984
|
+
|
|
985
|
+
if (info[1].IsNumber()) {
|
|
986
|
+
len = info[1].As<Napi::Number>().Int64Value();
|
|
987
|
+
} else if (info[1].IsString()) {
|
|
988
|
+
countedby = info[1];
|
|
989
|
+
|
|
990
|
+
if (info.Length() >= 4 && !IsNullOrUndefined(info[3])) {
|
|
991
|
+
len = info[3].As<Napi::Number>().Int64Value();
|
|
992
|
+
}
|
|
993
|
+
} else {
|
|
994
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for length, expected integer or string", GetValueType(instance, info[1]));
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
if (countedby.IsEmpty() && len <= 0) {
|
|
948
998
|
ThrowError<Napi::TypeError>(env, "Array length must be positive and non-zero");
|
|
949
999
|
return env.Null();
|
|
1000
|
+
} else if (len < 0) {
|
|
1001
|
+
ThrowError<Napi::TypeError>(env, "Array length must be positive");
|
|
1002
|
+
return env.Null();
|
|
950
1003
|
}
|
|
951
1004
|
if (len > instance->config.max_type_size / ref->size) {
|
|
952
1005
|
ThrowError<Napi::TypeError>(env, "Array length is too high (max = %1)", instance->config.max_type_size / ref->size);
|
|
953
1006
|
return env.Null();
|
|
954
1007
|
}
|
|
955
1008
|
|
|
956
|
-
|
|
1009
|
+
TypeInfo *type = nullptr;
|
|
957
1010
|
|
|
958
1011
|
if (info.Length() >= 3 && !IsNullOrUndefined(info[2])) {
|
|
959
1012
|
if (!info[2].IsString()) {
|
|
@@ -990,6 +1043,11 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
990
1043
|
type = MakeArrayType(instance, ref, len);
|
|
991
1044
|
}
|
|
992
1045
|
|
|
1046
|
+
if (!countedby.IsEmpty()) {
|
|
1047
|
+
Napi::String str = countedby.As<Napi::String>();
|
|
1048
|
+
type->countedby = DuplicateString(str.Utf8Value().c_str(), &instance->str_alloc).ptr;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
993
1051
|
return WrapType(env, instance, type);
|
|
994
1052
|
}
|
|
995
1053
|
|
|
@@ -1334,6 +1392,9 @@ static Napi::Value GetTypeDefinition(const Napi::CallbackInfo &info)
|
|
|
1334
1392
|
obj.Set("name", member.name);
|
|
1335
1393
|
obj.Set("type", WrapType(env, instance, member.type));
|
|
1336
1394
|
obj.Set("offset", member.offset);
|
|
1395
|
+
if (member.countedby >= 0) {
|
|
1396
|
+
obj.Set("countedBy", type->members[member.countedby].name);
|
|
1397
|
+
}
|
|
1337
1398
|
|
|
1338
1399
|
members.Set(member.name, obj);
|
|
1339
1400
|
}
|
package/src/koffi/src/ffi.hh
CHANGED
|
@@ -147,6 +147,7 @@ struct TypeInfo {
|
|
|
147
147
|
const FunctionInfo *proto; // Callback only
|
|
148
148
|
} ref;
|
|
149
149
|
ArrayHint hint; // Array only
|
|
150
|
+
const char *countedby; // Pointer or array
|
|
150
151
|
|
|
151
152
|
mutable Napi::FunctionReference construct; // Union only
|
|
152
153
|
mutable Napi::ObjectReference defn;
|
|
@@ -158,6 +159,7 @@ struct RecordMember {
|
|
|
158
159
|
const char *name;
|
|
159
160
|
const TypeInfo *type;
|
|
160
161
|
int32_t offset;
|
|
162
|
+
Size countedby;
|
|
161
163
|
};
|
|
162
164
|
|
|
163
165
|
struct LibraryHolder {
|
package/src/koffi/src/parser.cc
CHANGED
|
@@ -164,9 +164,20 @@ const TypeInfo *PrototypeParser::ParseType(int *out_directions)
|
|
|
164
164
|
}
|
|
165
165
|
offset--;
|
|
166
166
|
|
|
167
|
+
if (out_directions && offset > start) {
|
|
168
|
+
int directions = ResolveDirections(tokens[start]);
|
|
169
|
+
|
|
170
|
+
if (directions) {
|
|
171
|
+
*out_directions = directions;
|
|
172
|
+
start++;
|
|
173
|
+
} else {
|
|
174
|
+
*out_directions = 1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
167
178
|
while (offset >= start) {
|
|
168
179
|
Span<const char> str = MakeSpan(tokens[start].ptr, tokens[offset].end() - tokens[start].ptr);
|
|
169
|
-
const TypeInfo *type = ResolveType(env, str
|
|
180
|
+
const TypeInfo *type = ResolveType(env, str);
|
|
170
181
|
|
|
171
182
|
if (type) {
|
|
172
183
|
offset++;
|
package/src/koffi/src/util.cc
CHANGED
|
@@ -110,6 +110,45 @@ void MagicUnion::Setter(const Napi::CallbackInfo &info, const Napi::Value &value
|
|
|
110
110
|
raw.Clear();
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
static inline bool IsIdentifierStart(char c)
|
|
114
|
+
{
|
|
115
|
+
return IsAsciiAlpha(c) || c == '_';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static inline bool IsIdentifierChar(char c)
|
|
119
|
+
{
|
|
120
|
+
return IsAsciiAlphaOrDigit(c) || c == '_';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static inline Span<const char> SplitIdentifier(Span<const char> str)
|
|
124
|
+
{
|
|
125
|
+
Size offset = 0;
|
|
126
|
+
|
|
127
|
+
if (str.len && IsIdentifierStart(str[0])) {
|
|
128
|
+
offset++;
|
|
129
|
+
|
|
130
|
+
while (offset < str.len && IsIdentifierChar(str[offset])) {
|
|
131
|
+
offset++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
Span<const char> token = str.Take(0, offset);
|
|
136
|
+
return token;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
int ResolveDirections(Span<const char> str)
|
|
140
|
+
{
|
|
141
|
+
if (str == "_In_") {
|
|
142
|
+
return 1;
|
|
143
|
+
} else if (str == "_Out_") {
|
|
144
|
+
return 2;
|
|
145
|
+
} else if (str == "_Inout_") {
|
|
146
|
+
return 3;
|
|
147
|
+
} else {
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
113
152
|
const TypeInfo *ResolveType(Napi::Value value, int *out_directions)
|
|
114
153
|
{
|
|
115
154
|
Napi::Env env = value.Env();
|
|
@@ -117,12 +156,27 @@ const TypeInfo *ResolveType(Napi::Value value, int *out_directions)
|
|
|
117
156
|
|
|
118
157
|
if (value.IsString()) {
|
|
119
158
|
std::string str = value.As<Napi::String>();
|
|
159
|
+
Span<const char> remain = str.c_str();
|
|
120
160
|
|
|
121
161
|
// Quick path for known types (int, float *, etc.)
|
|
122
|
-
const TypeInfo *type = instance->types_map.FindValue(
|
|
162
|
+
const TypeInfo *type = instance->types_map.FindValue(remain.ptr, nullptr);
|
|
123
163
|
|
|
124
164
|
if (!type || (type->flags & (int)TypeFlag::IsIncomplete)) {
|
|
125
|
-
|
|
165
|
+
if (out_directions) {
|
|
166
|
+
Span<const char> prefix = SplitIdentifier(remain);
|
|
167
|
+
int directions = ResolveDirections(prefix);
|
|
168
|
+
|
|
169
|
+
if (directions) {
|
|
170
|
+
remain = remain.Take(prefix.len, remain.len - prefix.len);
|
|
171
|
+
remain = TrimStrLeft(remain);
|
|
172
|
+
|
|
173
|
+
*out_directions = directions;
|
|
174
|
+
} else {
|
|
175
|
+
*out_directions = 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
type = ResolveType(env, remain.ptr);
|
|
126
180
|
|
|
127
181
|
if (!type) {
|
|
128
182
|
if (!env.IsExceptionPending()) {
|
|
@@ -133,10 +187,10 @@ const TypeInfo *ResolveType(Napi::Value value, int *out_directions)
|
|
|
133
187
|
|
|
134
188
|
// Cache for quick future access
|
|
135
189
|
bool inserted;
|
|
136
|
-
auto bucket = instance->types_map.TrySetDefault(
|
|
190
|
+
auto bucket = instance->types_map.TrySetDefault(remain.ptr, &inserted);
|
|
137
191
|
|
|
138
192
|
if (inserted) {
|
|
139
|
-
bucket->key = DuplicateString(
|
|
193
|
+
bucket->key = DuplicateString(remain, &instance->str_alloc).ptr;
|
|
140
194
|
bucket->value = type;
|
|
141
195
|
}
|
|
142
196
|
} else if (out_directions) {
|
|
@@ -155,6 +209,7 @@ const TypeInfo *ResolveType(Napi::Value value, int *out_directions)
|
|
|
155
209
|
Size delta = (uint8_t *)raw - (uint8_t *)type;
|
|
156
210
|
*out_directions = 1 + (int)delta;
|
|
157
211
|
}
|
|
212
|
+
|
|
158
213
|
return type;
|
|
159
214
|
} else {
|
|
160
215
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value as type specifier, expected string or type", GetValueType(instance, value));
|
|
@@ -162,33 +217,7 @@ const TypeInfo *ResolveType(Napi::Value value, int *out_directions)
|
|
|
162
217
|
}
|
|
163
218
|
}
|
|
164
219
|
|
|
165
|
-
|
|
166
|
-
{
|
|
167
|
-
return IsAsciiAlpha(c) || c == '_';
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
static inline bool IsIdentifierChar(char c)
|
|
171
|
-
{
|
|
172
|
-
return IsAsciiAlphaOrDigit(c) || c == '_';
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
static inline Span<const char> SplitIdentifier(Span<const char> str)
|
|
176
|
-
{
|
|
177
|
-
Size offset = 0;
|
|
178
|
-
|
|
179
|
-
if (str.len && IsIdentifierStart(str[0])) {
|
|
180
|
-
offset++;
|
|
181
|
-
|
|
182
|
-
while (offset < str.len && IsIdentifierChar(str[offset])) {
|
|
183
|
-
offset++;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
Span<const char> token = str.Take(0, offset);
|
|
188
|
-
return token;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_directions)
|
|
220
|
+
const TypeInfo *ResolveType(Napi::Env env, Span<const char> str)
|
|
192
221
|
{
|
|
193
222
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
194
223
|
|
|
@@ -196,28 +225,6 @@ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_direct
|
|
|
196
225
|
LocalArray<Size, 8> arrays;
|
|
197
226
|
uint8_t disposables = 0;
|
|
198
227
|
|
|
199
|
-
// Consume parameter direction qualifier
|
|
200
|
-
if (out_directions) {
|
|
201
|
-
if (str.len && str[0] == '_') {
|
|
202
|
-
Span<const char> qualifier = SplitIdentifier(str);
|
|
203
|
-
|
|
204
|
-
if (qualifier == "_In_") {
|
|
205
|
-
*out_directions = 1;
|
|
206
|
-
str = str.Take(5, str.len - 5);
|
|
207
|
-
} else if (qualifier == "_Out_") {
|
|
208
|
-
*out_directions = 2;
|
|
209
|
-
str = str.Take(6, str.len - 6);
|
|
210
|
-
} else if (qualifier == "_Inout_") {
|
|
211
|
-
*out_directions = 3;
|
|
212
|
-
str = str.Take(8, str.len - 8);
|
|
213
|
-
} else {
|
|
214
|
-
*out_directions = 1;
|
|
215
|
-
}
|
|
216
|
-
} else {
|
|
217
|
-
*out_directions = 1;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
228
|
Span<const char> name;
|
|
222
229
|
Span<const char> after;
|
|
223
230
|
{
|
|
@@ -383,7 +390,7 @@ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_direct
|
|
|
383
390
|
return type;
|
|
384
391
|
}
|
|
385
392
|
|
|
386
|
-
|
|
393
|
+
TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *ref, int count)
|
|
387
394
|
{
|
|
388
395
|
RG_ASSERT(count >= 1);
|
|
389
396
|
|
|
@@ -404,6 +411,7 @@ const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *ref, int
|
|
|
404
411
|
type->size = RG_SIZE(void *);
|
|
405
412
|
type->align = RG_SIZE(void *);
|
|
406
413
|
type->ref.type = ref;
|
|
414
|
+
type->hint = (ref->flags & (int)TypeFlag::HasTypedArray) ? ArrayHint::Typed : ArrayHint::Array;
|
|
407
415
|
} else {
|
|
408
416
|
type->primitive = PrimitiveKind::Callback;
|
|
409
417
|
type->size = RG_SIZE(void *);
|
|
@@ -418,13 +426,12 @@ const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *ref, int
|
|
|
418
426
|
ref = bucket->value;
|
|
419
427
|
}
|
|
420
428
|
|
|
421
|
-
return ref;
|
|
429
|
+
return (TypeInfo *)ref;
|
|
422
430
|
}
|
|
423
431
|
|
|
424
|
-
static
|
|
425
|
-
ArrayHint hint, bool insert)
|
|
432
|
+
static TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len, ArrayHint hint, bool insert)
|
|
426
433
|
{
|
|
427
|
-
RG_ASSERT(len
|
|
434
|
+
RG_ASSERT(len >= 0);
|
|
428
435
|
RG_ASSERT(len <= instance->config.max_type_size / ref->size);
|
|
429
436
|
|
|
430
437
|
TypeInfo *type = instance->types.AppendDefault();
|
|
@@ -446,7 +453,7 @@ static const TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref
|
|
|
446
453
|
return type;
|
|
447
454
|
}
|
|
448
455
|
|
|
449
|
-
|
|
456
|
+
TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len)
|
|
450
457
|
{
|
|
451
458
|
ArrayHint hint = {};
|
|
452
459
|
|
|
@@ -461,7 +468,7 @@ const TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size
|
|
|
461
468
|
return MakeArrayType(instance, ref, len, hint, true);
|
|
462
469
|
}
|
|
463
470
|
|
|
464
|
-
|
|
471
|
+
TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len, ArrayHint hint)
|
|
465
472
|
{
|
|
466
473
|
return MakeArrayType(instance, ref, len, hint, false);
|
|
467
474
|
}
|
|
@@ -679,6 +686,86 @@ Napi::Object DecodeObject(Napi::Env env, const uint8_t *origin, const TypeInfo *
|
|
|
679
686
|
return obj;
|
|
680
687
|
}
|
|
681
688
|
|
|
689
|
+
static uint32_t DecodeDynamicLength(const uint8_t *origin, const RecordMember &by)
|
|
690
|
+
{
|
|
691
|
+
const uint8_t *src = origin + by.offset;
|
|
692
|
+
|
|
693
|
+
switch (by.type->primitive) {
|
|
694
|
+
case PrimitiveKind::Int8: {
|
|
695
|
+
int8_t i = *(int8_t *)src;
|
|
696
|
+
return (uint32_t)i;
|
|
697
|
+
} break;
|
|
698
|
+
case PrimitiveKind::UInt8: {
|
|
699
|
+
uint8_t u = *(uint8_t *)src;
|
|
700
|
+
return (uint32_t)u;
|
|
701
|
+
} break;
|
|
702
|
+
case PrimitiveKind::Int16: {
|
|
703
|
+
int16_t i = *(int16_t *)src;
|
|
704
|
+
return (uint32_t)i;
|
|
705
|
+
} break;
|
|
706
|
+
case PrimitiveKind::Int16S: {
|
|
707
|
+
int16_t i = ReverseBytes(*(int16_t *)src);
|
|
708
|
+
return (uint32_t)i;
|
|
709
|
+
} break;
|
|
710
|
+
case PrimitiveKind::UInt16: {
|
|
711
|
+
uint16_t u = *(uint16_t *)src;
|
|
712
|
+
return (uint32_t)u;
|
|
713
|
+
} break;
|
|
714
|
+
case PrimitiveKind::UInt16S: {
|
|
715
|
+
uint16_t u = ReverseBytes(*(uint16_t *)src);
|
|
716
|
+
return (uint32_t)u;
|
|
717
|
+
} break;
|
|
718
|
+
case PrimitiveKind::Int32: {
|
|
719
|
+
int32_t i = *(int32_t *)src;
|
|
720
|
+
return (uint32_t)i;
|
|
721
|
+
} break;
|
|
722
|
+
case PrimitiveKind::Int32S: {
|
|
723
|
+
int32_t i = ReverseBytes(*(int32_t *)src);
|
|
724
|
+
return (uint32_t)i;
|
|
725
|
+
} break;
|
|
726
|
+
case PrimitiveKind::UInt32: {
|
|
727
|
+
uint32_t u = *(uint32_t *)src;
|
|
728
|
+
return (uint32_t)u;
|
|
729
|
+
} break;
|
|
730
|
+
case PrimitiveKind::UInt32S: {
|
|
731
|
+
uint32_t u = ReverseBytes(*(uint32_t *)src);
|
|
732
|
+
return (uint32_t)u;
|
|
733
|
+
} break;
|
|
734
|
+
case PrimitiveKind::Int64: {
|
|
735
|
+
int64_t i = *(int64_t *)src;
|
|
736
|
+
return (uint32_t)i;
|
|
737
|
+
} break;
|
|
738
|
+
case PrimitiveKind::Int64S: {
|
|
739
|
+
int64_t i = ReverseBytes(*(int64_t *)src);
|
|
740
|
+
return (uint32_t)i;
|
|
741
|
+
} break;
|
|
742
|
+
case PrimitiveKind::UInt64: {
|
|
743
|
+
uint64_t u = *(uint64_t *)src;
|
|
744
|
+
return (uint32_t)u;
|
|
745
|
+
} break;
|
|
746
|
+
case PrimitiveKind::UInt64S: {
|
|
747
|
+
uint64_t u = ReverseBytes(*(uint64_t *)src);
|
|
748
|
+
return (uint32_t)u;
|
|
749
|
+
} break;
|
|
750
|
+
|
|
751
|
+
case PrimitiveKind::Void:
|
|
752
|
+
case PrimitiveKind::Bool:
|
|
753
|
+
case PrimitiveKind::String:
|
|
754
|
+
case PrimitiveKind::String16:
|
|
755
|
+
case PrimitiveKind::String32:
|
|
756
|
+
case PrimitiveKind::Pointer:
|
|
757
|
+
case PrimitiveKind::Callback:
|
|
758
|
+
case PrimitiveKind::Record:
|
|
759
|
+
case PrimitiveKind::Union:
|
|
760
|
+
case PrimitiveKind::Array:
|
|
761
|
+
case PrimitiveKind::Float32:
|
|
762
|
+
case PrimitiveKind::Float64:
|
|
763
|
+
case PrimitiveKind::Prototype: { RG_UNREACHABLE(); } break;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
RG_UNREACHABLE();
|
|
767
|
+
}
|
|
768
|
+
|
|
682
769
|
void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type)
|
|
683
770
|
{
|
|
684
771
|
Napi::Env env = obj.Env();
|
|
@@ -786,7 +873,13 @@ void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type)
|
|
|
786
873
|
case PrimitiveKind::Callback: {
|
|
787
874
|
void *ptr2 = *(void **)src;
|
|
788
875
|
|
|
789
|
-
if (
|
|
876
|
+
if (member.countedby >= 0) {
|
|
877
|
+
const RecordMember &by = type->members[member.countedby];
|
|
878
|
+
uint32_t len = DecodeDynamicLength(origin, by);
|
|
879
|
+
|
|
880
|
+
Napi::Value value = DecodeArray(env, (const uint8_t *)ptr2, member.type, len);
|
|
881
|
+
obj.Set(member.name, value);
|
|
882
|
+
} else if (ptr2) {
|
|
790
883
|
Napi::External<void> external = Napi::External<void>::New(env, ptr2);
|
|
791
884
|
SetValueTag(instance, external, member.type->ref.marker);
|
|
792
885
|
|
|
@@ -805,8 +898,16 @@ void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type)
|
|
|
805
898
|
obj.Set(member.name, obj2);
|
|
806
899
|
} break;
|
|
807
900
|
case PrimitiveKind::Array: {
|
|
808
|
-
|
|
809
|
-
|
|
901
|
+
if (member.countedby >= 0) {
|
|
902
|
+
const RecordMember &by = type->members[member.countedby];
|
|
903
|
+
uint32_t len = DecodeDynamicLength(origin, by);
|
|
904
|
+
|
|
905
|
+
Napi::Value value = DecodeArray(env, src, member.type, len);
|
|
906
|
+
obj.Set(member.name, value);
|
|
907
|
+
} else {
|
|
908
|
+
Napi::Value value = DecodeArray(env, src, member.type);
|
|
909
|
+
obj.Set(member.name, value);
|
|
910
|
+
}
|
|
810
911
|
} break;
|
|
811
912
|
case PrimitiveKind::Float32: {
|
|
812
913
|
float f = *(float *)src;
|
|
@@ -822,13 +923,9 @@ void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type)
|
|
|
822
923
|
}
|
|
823
924
|
}
|
|
824
925
|
|
|
825
|
-
Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *type)
|
|
926
|
+
Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *type, uint32_t len)
|
|
826
927
|
{
|
|
827
928
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
828
|
-
|
|
829
|
-
RG_ASSERT(type->primitive == PrimitiveKind::Array);
|
|
830
|
-
|
|
831
|
-
uint32_t len = type->size / type->ref.type->size;
|
|
832
929
|
Size offset = 0;
|
|
833
930
|
|
|
834
931
|
#define POP_ARRAY(SetCode) \
|
|
@@ -1013,6 +1110,14 @@ Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *ty
|
|
|
1013
1110
|
RG_UNREACHABLE();
|
|
1014
1111
|
}
|
|
1015
1112
|
|
|
1113
|
+
Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *type)
|
|
1114
|
+
{
|
|
1115
|
+
RG_ASSERT(type->primitive == PrimitiveKind::Array);
|
|
1116
|
+
|
|
1117
|
+
uint32_t len = type->size / type->ref.type->size;
|
|
1118
|
+
return DecodeArray(env, origin, type, len);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1016
1121
|
void DecodeNormalArray(Napi::Array array, const uint8_t *origin, const TypeInfo *ref)
|
|
1017
1122
|
{
|
|
1018
1123
|
Napi::Env env = array.Env();
|
package/src/koffi/src/util.hh
CHANGED
|
@@ -86,12 +86,13 @@ static inline bool IsRegularSize(Size size, Size max)
|
|
|
86
86
|
return regular;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
int ResolveDirections(Span<const char> str);
|
|
89
90
|
const TypeInfo *ResolveType(Napi::Value value, int *out_directions = nullptr);
|
|
90
|
-
const TypeInfo *ResolveType(Napi::Env env, Span<const char> str
|
|
91
|
+
const TypeInfo *ResolveType(Napi::Env env, Span<const char> str);
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *ref, int count = 1);
|
|
94
|
+
TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len);
|
|
95
|
+
TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len, ArrayHint hint);
|
|
95
96
|
|
|
96
97
|
Napi::External<TypeInfo> WrapType(Napi::Env env, InstanceData *instance, const TypeInfo *type);
|
|
97
98
|
|
|
@@ -197,6 +198,7 @@ static inline Napi::String MakeStringFromUTF32(Napi::Env env, const char32_t *pt
|
|
|
197
198
|
|
|
198
199
|
Napi::Object DecodeObject(Napi::Env env, const uint8_t *origin, const TypeInfo *type);
|
|
199
200
|
void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type);
|
|
201
|
+
Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *type, uint32_t len);
|
|
200
202
|
Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *type);
|
|
201
203
|
void DecodeNormalArray(Napi::Array array, const uint8_t *origin, const TypeInfo *ref);
|
|
202
204
|
void DecodeBuffer(Span<uint8_t> buffer, const uint8_t *origin, const TypeInfo *ref);
|