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.
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
- export function load(path: string | null): IKoffiLib;
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
- export function config(): Record<string, unknown>;
159
- export function config(cfg: Record<string, unknown>): Record<string, unknown>;
160
- export function stats(): Record<string, unknown>;
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.13.0",
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.13.0",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.13.0",
3
+ "version": "2.14.0-beta.2",
4
4
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
5
5
  "keywords": [
6
6
  "foreign",
@@ -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;
@@ -138,6 +138,8 @@ private:
138
138
  template <typename T = uint8_t>
139
139
  T *AllocHeap(Size size, Size align);
140
140
 
141
+ bool CheckDynamicLength(Napi::Object obj, Size element, const char *countedby, Napi::Value value);
142
+
141
143
  void PopOutArguments();
142
144
  };
143
145
 
@@ -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 *type = ResolveType(info[skip]);
637
- if (!type)
664
+ const TypeInfo *ref = ResolveType(info[skip]);
665
+ if (!ref)
638
666
  return env.Null();
639
667
 
640
- int count = 0;
641
- if (info.Length() >= 2u + skip) {
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
- count = info[1 + skip].As<Napi::Number>();
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
- if (count < 1 || count > 4) {
650
- ThrowError<Napi::TypeError>(env, "Value of count must be between 1 and 4");
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, type, count);
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
- if (len <= 0) {
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
- const TypeInfo *type = nullptr;
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
  }
@@ -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 {
@@ -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, out_directions);
180
+ const TypeInfo *type = ResolveType(env, str);
170
181
 
171
182
  if (type) {
172
183
  offset++;
@@ -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(str.c_str(), nullptr);
162
+ const TypeInfo *type = instance->types_map.FindValue(remain.ptr, nullptr);
123
163
 
124
164
  if (!type || (type->flags & (int)TypeFlag::IsIncomplete)) {
125
- type = ResolveType(env, str.c_str(), out_directions);
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(str.c_str(), &inserted);
190
+ auto bucket = instance->types_map.TrySetDefault(remain.ptr, &inserted);
137
191
 
138
192
  if (inserted) {
139
- bucket->key = DuplicateString(str.c_str(), &instance->str_alloc).ptr;
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
- static inline bool IsIdentifierStart(char c)
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
- const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *ref, int count)
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 const TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len,
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 > 0);
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
- const TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len)
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
- const TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len, ArrayHint hint)
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 (ptr2) {
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
- Napi::Value value = DecodeArray(env, src, member.type);
809
- obj.Set(member.name, value);
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();
@@ -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, int *out_directions = nullptr);
91
+ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str);
91
92
 
92
- const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *ref, int count = 1);
93
- const TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len);
94
- const TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len, ArrayHint hint);
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);