koffi 2.13.0 → 2.14.0-beta.2
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 +1 -1
- package/indirect.js +1 -1
- package/package.json +1 -1
- package/src/koffi/src/call.cc +46 -0
- package/src/koffi/src/call.hh +2 -0
- package/src/koffi/src/ffi.cc +82 -26
- package/src/koffi/src/ffi.hh +2 -0
- package/src/koffi/src/parser.cc +12 -1
- package/src/koffi/src/util.cc +179 -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
|
@@ -402,7 +402,7 @@ 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.2",
|
|
406
406
|
description: "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
407
407
|
keywords: [
|
|
408
408
|
"foreign",
|
package/indirect.js
CHANGED
|
@@ -402,7 +402,7 @@ 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.2",
|
|
406
406
|
description: "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
407
407
|
keywords: [
|
|
408
408
|
"foreign",
|
package/package.json
CHANGED
package/src/koffi/src/call.cc
CHANGED
|
@@ -523,6 +523,13 @@ bool CallData::PushObject(Napi::Object obj, const TypeInfo *type, uint8_t *origi
|
|
|
523
523
|
const RecordMember &member = members[i];
|
|
524
524
|
Napi::Value value = obj.Get(member.name);
|
|
525
525
|
|
|
526
|
+
if (member.countedby >= 0) {
|
|
527
|
+
const char *countedby = members[member.countedby].name;
|
|
528
|
+
|
|
529
|
+
if (!CheckDynamicLength(obj, member.type->ref.type->size, countedby, value)) [[unlikely]]
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
|
|
526
533
|
if (value.IsUndefined())
|
|
527
534
|
continue;
|
|
528
535
|
|
|
@@ -1359,6 +1366,45 @@ void CallData::DumpForward(const FunctionInfo *func) const
|
|
|
1359
1366
|
DumpMemory("Heap", heap);
|
|
1360
1367
|
}
|
|
1361
1368
|
|
|
1369
|
+
bool CallData::CheckDynamicLength(Napi::Object obj, Size element, const char *countedby, Napi::Value value)
|
|
1370
|
+
{
|
|
1371
|
+
int64_t expected = -1;
|
|
1372
|
+
int64_t len = -1;
|
|
1373
|
+
|
|
1374
|
+
// Get expected length
|
|
1375
|
+
{
|
|
1376
|
+
Napi::Value by = obj.Get(countedby);
|
|
1377
|
+
|
|
1378
|
+
if (!by.IsNumber() && !by.IsBigInt()) [[unlikely]] {
|
|
1379
|
+
ThrowError<Napi::Error>(env, "Unexpected %1 value for dynamic length, expected number", GetValueType(instance, by));
|
|
1380
|
+
return false;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
expected = GetNumber<int64_t>(by);
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// Get actual length
|
|
1387
|
+
if (value.IsArray()) {
|
|
1388
|
+
Napi::Array array = value.As<Napi::Array>();
|
|
1389
|
+
len = array.Length();
|
|
1390
|
+
} else if (value.IsTypedArray()) {
|
|
1391
|
+
Napi::TypedArray typed = value.As<Napi::TypedArray>();
|
|
1392
|
+
len = typed.ByteLength() / element;
|
|
1393
|
+
} else if (value.IsArrayBuffer()) {
|
|
1394
|
+
Napi::ArrayBuffer buffer = value.As<Napi::ArrayBuffer>();
|
|
1395
|
+
len = buffer.ByteLength() / element;
|
|
1396
|
+
} else {
|
|
1397
|
+
len = !IsNullOrUndefined(value);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
if (len != expected) {
|
|
1401
|
+
ThrowError<Napi::Error>(env, "Mismatched dynamic length between '%1' and actual array", countedby);
|
|
1402
|
+
return false;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
return true;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1362
1408
|
static inline Napi::Value GetReferenceValue(Napi::Env env, napi_ref ref)
|
|
1363
1409
|
{
|
|
1364
1410
|
napi_value value;
|
package/src/koffi/src/call.hh
CHANGED
package/src/koffi/src/ffi.cc
CHANGED
|
@@ -372,6 +372,28 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
|
|
|
372
372
|
type->members.Append(member);
|
|
373
373
|
}
|
|
374
374
|
|
|
375
|
+
for (RecordMember &member: type->members) {
|
|
376
|
+
const char *countedby = member.type->countedby;
|
|
377
|
+
|
|
378
|
+
if (countedby) {
|
|
379
|
+
const RecordMember *by = std::find_if(type->members.begin(), type->members.end(),
|
|
380
|
+
[&](const RecordMember &member) { return TestStr(member.name, countedby); });
|
|
381
|
+
|
|
382
|
+
if (by == type->members.end()) {
|
|
383
|
+
ThrowError<Napi::Error>(env, "Record type %1 does not have member '%2'", type->name, countedby);
|
|
384
|
+
return env.Null();
|
|
385
|
+
}
|
|
386
|
+
if (!IsInteger(by->type)) {
|
|
387
|
+
ThrowError<Napi::Error>(env, "Dynamic length member %1 is not an integer", countedby);
|
|
388
|
+
return env.Null();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
member.countedby = by - type->members.ptr;
|
|
392
|
+
} else {
|
|
393
|
+
member.countedby = -1;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
375
397
|
size = (int32_t)AlignLen(size, type->align);
|
|
376
398
|
if (!size) {
|
|
377
399
|
ThrowError<Napi::Error>(env, "Empty struct '%1' is not allowed in C", type->name);
|
|
@@ -507,11 +529,17 @@ static Napi::Value CreateUnionType(const Napi::CallbackInfo &info)
|
|
|
507
529
|
ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a member (maybe try %1 *)", member.type->name);
|
|
508
530
|
return env.Null();
|
|
509
531
|
}
|
|
532
|
+
if (member.type->countedby) {
|
|
533
|
+
ThrowError<Napi::TypeError>(env, "Cannot use dynamic-length array or pointer inside of union");
|
|
534
|
+
return env.Null();
|
|
535
|
+
}
|
|
510
536
|
|
|
511
537
|
align = align ? align : member.type->align;
|
|
512
538
|
size = std::max(size, member.type->size);
|
|
513
539
|
type->align = std::max(type->align, align);
|
|
514
540
|
|
|
541
|
+
member.countedby = -1;
|
|
542
|
+
|
|
515
543
|
if (TestStr(member.name, "_"))
|
|
516
544
|
continue;
|
|
517
545
|
|
|
@@ -633,39 +661,46 @@ static Napi::Value CreatePointerType(const Napi::CallbackInfo &info)
|
|
|
633
661
|
|
|
634
662
|
std::string name = named ? info[0].As<Napi::String>() : std::string();
|
|
635
663
|
|
|
636
|
-
const TypeInfo *
|
|
637
|
-
if (!
|
|
664
|
+
const TypeInfo *ref = ResolveType(info[skip]);
|
|
665
|
+
if (!ref)
|
|
638
666
|
return env.Null();
|
|
639
667
|
|
|
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
|
-
}
|
|
668
|
+
Napi::Value countedby;
|
|
669
|
+
int count = 1;
|
|
646
670
|
|
|
647
|
-
|
|
671
|
+
if (info.Length() >= 2u + skip) {
|
|
672
|
+
if (info[1 + skip].IsString()) {
|
|
673
|
+
countedby = info[1 + skip];
|
|
674
|
+
} else if (info[1 + skip].IsNumber()) {
|
|
675
|
+
count = info[1 + skip].As<Napi::Number>();
|
|
648
676
|
|
|
649
|
-
|
|
650
|
-
|
|
677
|
+
if (count < 1 || count > 4) {
|
|
678
|
+
ThrowError<Napi::TypeError>(env, "Value of count must be between 1 and 4");
|
|
679
|
+
return env.Null();
|
|
680
|
+
}
|
|
681
|
+
} else {
|
|
682
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for count, expected number", GetValueType(instance, info[1 + skip]));
|
|
651
683
|
return env.Null();
|
|
652
684
|
}
|
|
653
|
-
} else {
|
|
654
|
-
count = 1;
|
|
655
685
|
}
|
|
656
686
|
|
|
657
|
-
type = MakePointerType(instance,
|
|
687
|
+
TypeInfo *type = MakePointerType(instance, ref, count);
|
|
658
688
|
RG_ASSERT(type);
|
|
659
689
|
|
|
660
|
-
if (named) {
|
|
690
|
+
if (named || !countedby.IsEmpty()) {
|
|
661
691
|
TypeInfo *copy = instance->types.AppendDefault();
|
|
662
692
|
RG_DEFER_N(err_guard) { instance->types.RemoveLast(1); };
|
|
663
693
|
|
|
664
694
|
memcpy((void *)copy, type, RG_SIZE(*type));
|
|
665
|
-
copy->name = DuplicateString(name.c_str(), &instance->str_alloc).ptr;
|
|
695
|
+
copy->name = named ? DuplicateString(name.c_str(), &instance->str_alloc).ptr : copy->name;
|
|
696
|
+
|
|
697
|
+
if (!countedby.IsEmpty()) {
|
|
698
|
+
Napi::String str = countedby.As<Napi::String>();
|
|
699
|
+
copy->countedby = DuplicateString(str.Utf8Value().c_str(), &instance->str_alloc).ptr;
|
|
700
|
+
}
|
|
666
701
|
|
|
667
702
|
// If the insert succeeds, we cannot fail anymore
|
|
668
|
-
if (!MapType(env, instance, copy, copy->name))
|
|
703
|
+
if (named && !MapType(env, instance, copy, copy->name))
|
|
669
704
|
return env.Null();
|
|
670
705
|
err_guard.Disable();
|
|
671
706
|
|
|
@@ -931,29 +966,42 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
931
966
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
932
967
|
|
|
933
968
|
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]));
|
|
969
|
+
ThrowError<Napi::TypeError>(env, "Expected 2 to 3 arguments, got %1", info.Length());
|
|
939
970
|
return env.Null();
|
|
940
971
|
}
|
|
941
972
|
|
|
942
973
|
const TypeInfo *ref = ResolveType(info[0]);
|
|
943
|
-
int64_t len = info[1].As<Napi::Number>().Int64Value();
|
|
944
|
-
|
|
945
974
|
if (!ref)
|
|
946
975
|
return env.Null();
|
|
947
|
-
|
|
976
|
+
|
|
977
|
+
int64_t len = 0;
|
|
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];
|
|
984
|
+
|
|
985
|
+
if (info.Length() >= 4 && !IsNullOrUndefined(info[3])) {
|
|
986
|
+
len = info[3].As<Napi::Number>().Int64Value();
|
|
987
|
+
}
|
|
988
|
+
} else {
|
|
989
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for length, expected integer or string", GetValueType(instance, info[1]));
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if (countedby.IsEmpty() && len <= 0) {
|
|
948
993
|
ThrowError<Napi::TypeError>(env, "Array length must be positive and non-zero");
|
|
949
994
|
return env.Null();
|
|
995
|
+
} else if (len < 0) {
|
|
996
|
+
ThrowError<Napi::TypeError>(env, "Array length must be positive");
|
|
997
|
+
return env.Null();
|
|
950
998
|
}
|
|
951
999
|
if (len > instance->config.max_type_size / ref->size) {
|
|
952
1000
|
ThrowError<Napi::TypeError>(env, "Array length is too high (max = %1)", instance->config.max_type_size / ref->size);
|
|
953
1001
|
return env.Null();
|
|
954
1002
|
}
|
|
955
1003
|
|
|
956
|
-
|
|
1004
|
+
TypeInfo *type = nullptr;
|
|
957
1005
|
|
|
958
1006
|
if (info.Length() >= 3 && !IsNullOrUndefined(info[2])) {
|
|
959
1007
|
if (!info[2].IsString()) {
|
|
@@ -990,6 +1038,11 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
990
1038
|
type = MakeArrayType(instance, ref, len);
|
|
991
1039
|
}
|
|
992
1040
|
|
|
1041
|
+
if (!countedby.IsEmpty()) {
|
|
1042
|
+
Napi::String str = countedby.As<Napi::String>();
|
|
1043
|
+
type->countedby = DuplicateString(str.Utf8Value().c_str(), &instance->str_alloc).ptr;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
993
1046
|
return WrapType(env, instance, type);
|
|
994
1047
|
}
|
|
995
1048
|
|
|
@@ -1334,6 +1387,9 @@ static Napi::Value GetTypeDefinition(const Napi::CallbackInfo &info)
|
|
|
1334
1387
|
obj.Set("name", member.name);
|
|
1335
1388
|
obj.Set("type", WrapType(env, instance, member.type));
|
|
1336
1389
|
obj.Set("offset", member.offset);
|
|
1390
|
+
if (member.countedby >= 0) {
|
|
1391
|
+
obj.Set("countedBy", type->members[member.countedby].name);
|
|
1392
|
+
}
|
|
1337
1393
|
|
|
1338
1394
|
members.Set(member.name, obj);
|
|
1339
1395
|
}
|
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
|
}
|
|
@@ -476,6 +483,9 @@ Napi::External<TypeInfo> WrapType(Napi::Env env, InstanceData *instance, const T
|
|
|
476
483
|
|
|
477
484
|
bool CanPassType(const TypeInfo *type, int directions)
|
|
478
485
|
{
|
|
486
|
+
if (type->countedby)
|
|
487
|
+
return false;
|
|
488
|
+
|
|
479
489
|
if (directions & 2) {
|
|
480
490
|
if (type->primitive == PrimitiveKind::Pointer)
|
|
481
491
|
return true;
|
|
@@ -503,6 +513,9 @@ bool CanPassType(const TypeInfo *type, int directions)
|
|
|
503
513
|
|
|
504
514
|
bool CanReturnType(const TypeInfo *type)
|
|
505
515
|
{
|
|
516
|
+
if (type->countedby)
|
|
517
|
+
return false;
|
|
518
|
+
|
|
506
519
|
if (type->primitive == PrimitiveKind::Void && !TestStr(type->name, "void"))
|
|
507
520
|
return false;
|
|
508
521
|
if (type->primitive == PrimitiveKind::Array)
|
|
@@ -679,6 +692,86 @@ Napi::Object DecodeObject(Napi::Env env, const uint8_t *origin, const TypeInfo *
|
|
|
679
692
|
return obj;
|
|
680
693
|
}
|
|
681
694
|
|
|
695
|
+
static uint32_t DecodeDynamicLength(const uint8_t *origin, const RecordMember &by)
|
|
696
|
+
{
|
|
697
|
+
const uint8_t *src = origin + by.offset;
|
|
698
|
+
|
|
699
|
+
switch (by.type->primitive) {
|
|
700
|
+
case PrimitiveKind::Int8: {
|
|
701
|
+
int8_t i = *(int8_t *)src;
|
|
702
|
+
return (uint32_t)i;
|
|
703
|
+
} break;
|
|
704
|
+
case PrimitiveKind::UInt8: {
|
|
705
|
+
uint8_t u = *(uint8_t *)src;
|
|
706
|
+
return (uint32_t)u;
|
|
707
|
+
} break;
|
|
708
|
+
case PrimitiveKind::Int16: {
|
|
709
|
+
int16_t i = *(int16_t *)src;
|
|
710
|
+
return (uint32_t)i;
|
|
711
|
+
} break;
|
|
712
|
+
case PrimitiveKind::Int16S: {
|
|
713
|
+
int16_t i = ReverseBytes(*(int16_t *)src);
|
|
714
|
+
return (uint32_t)i;
|
|
715
|
+
} break;
|
|
716
|
+
case PrimitiveKind::UInt16: {
|
|
717
|
+
uint16_t u = *(uint16_t *)src;
|
|
718
|
+
return (uint32_t)u;
|
|
719
|
+
} break;
|
|
720
|
+
case PrimitiveKind::UInt16S: {
|
|
721
|
+
uint16_t u = ReverseBytes(*(uint16_t *)src);
|
|
722
|
+
return (uint32_t)u;
|
|
723
|
+
} break;
|
|
724
|
+
case PrimitiveKind::Int32: {
|
|
725
|
+
int32_t i = *(int32_t *)src;
|
|
726
|
+
return (uint32_t)i;
|
|
727
|
+
} break;
|
|
728
|
+
case PrimitiveKind::Int32S: {
|
|
729
|
+
int32_t i = ReverseBytes(*(int32_t *)src);
|
|
730
|
+
return (uint32_t)i;
|
|
731
|
+
} break;
|
|
732
|
+
case PrimitiveKind::UInt32: {
|
|
733
|
+
uint32_t u = *(uint32_t *)src;
|
|
734
|
+
return (uint32_t)u;
|
|
735
|
+
} break;
|
|
736
|
+
case PrimitiveKind::UInt32S: {
|
|
737
|
+
uint32_t u = ReverseBytes(*(uint32_t *)src);
|
|
738
|
+
return (uint32_t)u;
|
|
739
|
+
} break;
|
|
740
|
+
case PrimitiveKind::Int64: {
|
|
741
|
+
int64_t i = *(int64_t *)src;
|
|
742
|
+
return (uint32_t)i;
|
|
743
|
+
} break;
|
|
744
|
+
case PrimitiveKind::Int64S: {
|
|
745
|
+
int64_t i = ReverseBytes(*(int64_t *)src);
|
|
746
|
+
return (uint32_t)i;
|
|
747
|
+
} break;
|
|
748
|
+
case PrimitiveKind::UInt64: {
|
|
749
|
+
uint64_t u = *(uint64_t *)src;
|
|
750
|
+
return (uint32_t)u;
|
|
751
|
+
} break;
|
|
752
|
+
case PrimitiveKind::UInt64S: {
|
|
753
|
+
uint64_t u = ReverseBytes(*(uint64_t *)src);
|
|
754
|
+
return (uint32_t)u;
|
|
755
|
+
} break;
|
|
756
|
+
|
|
757
|
+
case PrimitiveKind::Void:
|
|
758
|
+
case PrimitiveKind::Bool:
|
|
759
|
+
case PrimitiveKind::String:
|
|
760
|
+
case PrimitiveKind::String16:
|
|
761
|
+
case PrimitiveKind::String32:
|
|
762
|
+
case PrimitiveKind::Pointer:
|
|
763
|
+
case PrimitiveKind::Callback:
|
|
764
|
+
case PrimitiveKind::Record:
|
|
765
|
+
case PrimitiveKind::Union:
|
|
766
|
+
case PrimitiveKind::Array:
|
|
767
|
+
case PrimitiveKind::Float32:
|
|
768
|
+
case PrimitiveKind::Float64:
|
|
769
|
+
case PrimitiveKind::Prototype: { RG_UNREACHABLE(); } break;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
RG_UNREACHABLE();
|
|
773
|
+
}
|
|
774
|
+
|
|
682
775
|
void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type)
|
|
683
776
|
{
|
|
684
777
|
Napi::Env env = obj.Env();
|
|
@@ -786,7 +879,13 @@ void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type)
|
|
|
786
879
|
case PrimitiveKind::Callback: {
|
|
787
880
|
void *ptr2 = *(void **)src;
|
|
788
881
|
|
|
789
|
-
if (
|
|
882
|
+
if (member.countedby >= 0) {
|
|
883
|
+
const RecordMember &by = type->members[member.countedby];
|
|
884
|
+
uint32_t len = DecodeDynamicLength(origin, by);
|
|
885
|
+
|
|
886
|
+
Napi::Value value = DecodeArray(env, (const uint8_t *)ptr2, member.type, len);
|
|
887
|
+
obj.Set(member.name, value);
|
|
888
|
+
} else if (ptr2) {
|
|
790
889
|
Napi::External<void> external = Napi::External<void>::New(env, ptr2);
|
|
791
890
|
SetValueTag(instance, external, member.type->ref.marker);
|
|
792
891
|
|
|
@@ -805,8 +904,16 @@ void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type)
|
|
|
805
904
|
obj.Set(member.name, obj2);
|
|
806
905
|
} break;
|
|
807
906
|
case PrimitiveKind::Array: {
|
|
808
|
-
|
|
809
|
-
|
|
907
|
+
if (member.countedby >= 0) {
|
|
908
|
+
const RecordMember &by = type->members[member.countedby];
|
|
909
|
+
uint32_t len = DecodeDynamicLength(origin, by);
|
|
910
|
+
|
|
911
|
+
Napi::Value value = DecodeArray(env, src, member.type, len);
|
|
912
|
+
obj.Set(member.name, value);
|
|
913
|
+
} else {
|
|
914
|
+
Napi::Value value = DecodeArray(env, src, member.type);
|
|
915
|
+
obj.Set(member.name, value);
|
|
916
|
+
}
|
|
810
917
|
} break;
|
|
811
918
|
case PrimitiveKind::Float32: {
|
|
812
919
|
float f = *(float *)src;
|
|
@@ -822,13 +929,9 @@ void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type)
|
|
|
822
929
|
}
|
|
823
930
|
}
|
|
824
931
|
|
|
825
|
-
Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *type)
|
|
932
|
+
Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *type, uint32_t len)
|
|
826
933
|
{
|
|
827
934
|
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
935
|
Size offset = 0;
|
|
833
936
|
|
|
834
937
|
#define POP_ARRAY(SetCode) \
|
|
@@ -1013,6 +1116,14 @@ Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *ty
|
|
|
1013
1116
|
RG_UNREACHABLE();
|
|
1014
1117
|
}
|
|
1015
1118
|
|
|
1119
|
+
Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *type)
|
|
1120
|
+
{
|
|
1121
|
+
RG_ASSERT(type->primitive == PrimitiveKind::Array);
|
|
1122
|
+
|
|
1123
|
+
uint32_t len = type->size / type->ref.type->size;
|
|
1124
|
+
return DecodeArray(env, origin, type, len);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1016
1127
|
void DecodeNormalArray(Napi::Array array, const uint8_t *origin, const TypeInfo *ref)
|
|
1017
1128
|
{
|
|
1018
1129
|
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);
|