koffi 1.2.4 → 1.3.0-rc.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.
Files changed (126) hide show
  1. package/README.md +7 -489
  2. package/benchmark/CMakeLists.txt +13 -9
  3. package/benchmark/raylib_node_raylib.js +67 -0
  4. package/build/qemu/1.3.0-rc.1/koffi_darwin_arm64.tar.gz +0 -0
  5. package/build/qemu/1.3.0-rc.1/koffi_darwin_x64.tar.gz +0 -0
  6. package/build/qemu/1.3.0-rc.1/koffi_freebsd_arm64.tar.gz +0 -0
  7. package/build/qemu/1.3.0-rc.1/koffi_freebsd_ia32.tar.gz +0 -0
  8. package/build/qemu/1.3.0-rc.1/koffi_freebsd_x64.tar.gz +0 -0
  9. package/build/qemu/1.3.0-rc.1/koffi_linux_arm32hf.tar.gz +0 -0
  10. package/build/qemu/1.3.0-rc.1/koffi_linux_arm64.tar.gz +0 -0
  11. package/build/qemu/1.3.0-rc.1/koffi_linux_ia32.tar.gz +0 -0
  12. package/build/qemu/1.3.0-rc.1/koffi_linux_riscv64hf64.tar.gz +0 -0
  13. package/build/qemu/1.3.0-rc.1/koffi_linux_x64.tar.gz +0 -0
  14. package/build/qemu/1.3.0-rc.1/koffi_openbsd_ia32.tar.gz +0 -0
  15. package/build/qemu/1.3.0-rc.1/koffi_openbsd_x64.tar.gz +0 -0
  16. package/build/qemu/1.3.0-rc.1/koffi_win32_arm64.tar.gz +0 -0
  17. package/build/qemu/1.3.0-rc.1/koffi_win32_ia32.tar.gz +0 -0
  18. package/build/qemu/1.3.0-rc.1/koffi_win32_x64.tar.gz +0 -0
  19. package/doc/Makefile +20 -0
  20. package/doc/_static/bench_linux.png +0 -0
  21. package/doc/_static/bench_windows.png +0 -0
  22. package/doc/_static/custom.css +22 -0
  23. package/doc/benchmarks.md +113 -0
  24. package/doc/benchmarks.xlsx +0 -0
  25. package/doc/conf.py +54 -0
  26. package/doc/contribute.md +115 -0
  27. package/doc/dist/doctrees/benchmarks.doctree +0 -0
  28. package/doc/dist/doctrees/contribute.doctree +0 -0
  29. package/doc/dist/doctrees/environment.pickle +0 -0
  30. package/doc/dist/doctrees/functions.doctree +0 -0
  31. package/doc/dist/doctrees/index.doctree +0 -0
  32. package/doc/dist/doctrees/memory.doctree +0 -0
  33. package/doc/dist/doctrees/platforms.doctree +0 -0
  34. package/doc/dist/doctrees/start.doctree +0 -0
  35. package/doc/dist/doctrees/types.doctree +0 -0
  36. package/doc/dist/html/.buildinfo +4 -0
  37. package/doc/dist/html/_sources/benchmarks.md.txt +113 -0
  38. package/doc/dist/html/_sources/contribute.md.txt +115 -0
  39. package/doc/dist/html/_sources/functions.md.txt +224 -0
  40. package/doc/dist/html/_sources/index.rst.txt +33 -0
  41. package/doc/dist/html/_sources/memory.md.txt +29 -0
  42. package/doc/dist/html/_sources/platforms.md.txt +17 -0
  43. package/doc/dist/html/_sources/start.md.txt +89 -0
  44. package/doc/dist/html/_sources/types.md.txt +514 -0
  45. package/doc/dist/html/_static/_sphinx_javascript_frameworks_compat.js +134 -0
  46. package/doc/dist/html/_static/basic.css +932 -0
  47. package/doc/dist/html/_static/bench_linux.png +0 -0
  48. package/doc/dist/html/_static/bench_windows.png +0 -0
  49. package/doc/dist/html/_static/custom.css +22 -0
  50. package/doc/dist/html/_static/debug.css +69 -0
  51. package/doc/dist/html/_static/doctools.js +264 -0
  52. package/doc/dist/html/_static/documentation_options.js +14 -0
  53. package/doc/dist/html/_static/file.png +0 -0
  54. package/doc/dist/html/_static/jquery-3.6.0.js +10881 -0
  55. package/doc/dist/html/_static/jquery.js +2 -0
  56. package/doc/dist/html/_static/language_data.js +199 -0
  57. package/doc/dist/html/_static/minus.png +0 -0
  58. package/doc/dist/html/_static/plus.png +0 -0
  59. package/doc/dist/html/_static/pygments.css +252 -0
  60. package/doc/dist/html/_static/scripts/furo-extensions.js +0 -0
  61. package/doc/dist/html/_static/scripts/furo.js +3 -0
  62. package/doc/dist/html/_static/scripts/furo.js.LICENSE.txt +7 -0
  63. package/doc/dist/html/_static/scripts/furo.js.map +1 -0
  64. package/doc/dist/html/_static/searchtools.js +531 -0
  65. package/doc/dist/html/_static/skeleton.css +296 -0
  66. package/doc/dist/html/_static/styles/furo-extensions.css +2 -0
  67. package/doc/dist/html/_static/styles/furo-extensions.css.map +1 -0
  68. package/doc/dist/html/_static/styles/furo.css +2 -0
  69. package/doc/dist/html/_static/styles/furo.css.map +1 -0
  70. package/doc/dist/html/_static/underscore-1.13.1.js +2042 -0
  71. package/doc/dist/html/_static/underscore.js +6 -0
  72. package/doc/dist/html/benchmarks.html +547 -0
  73. package/doc/dist/html/contribute.html +382 -0
  74. package/doc/dist/html/functions.html +530 -0
  75. package/doc/dist/html/genindex.html +249 -0
  76. package/doc/dist/html/index.html +342 -0
  77. package/doc/dist/html/memory.html +337 -0
  78. package/doc/dist/html/objects.inv +0 -0
  79. package/doc/dist/html/platforms.html +332 -0
  80. package/doc/dist/html/search.html +257 -0
  81. package/doc/dist/html/searchindex.js +1 -0
  82. package/doc/dist/html/start.html +367 -0
  83. package/doc/dist/html/types.html +1001 -0
  84. package/doc/functions.md +224 -0
  85. package/doc/index.rst +33 -0
  86. package/doc/make.bat +35 -0
  87. package/doc/memory.md +29 -0
  88. package/doc/platforms.md +17 -0
  89. package/doc/start.md +89 -0
  90. package/doc/types.md +514 -0
  91. package/package.json +5 -2
  92. package/qemu/qemu.js +41 -27
  93. package/qemu/registry/machines.json +59 -79
  94. package/qemu/registry/sha256sum.txt +4 -4
  95. package/src/abi_arm32.cc +20 -48
  96. package/src/abi_arm64.cc +18 -46
  97. package/src/abi_arm64_fwd.S +5 -0
  98. package/src/abi_riscv64.cc +19 -47
  99. package/src/abi_x64_sysv.cc +18 -46
  100. package/src/abi_x64_win.cc +19 -47
  101. package/src/abi_x86.cc +21 -49
  102. package/src/call.cc +505 -242
  103. package/src/call.hh +14 -7
  104. package/src/ffi.cc +47 -26
  105. package/src/ffi.hh +1 -1
  106. package/src/parser.cc +2 -20
  107. package/src/util.cc +50 -11
  108. package/src/util.hh +2 -0
  109. package/test/misc.c +31 -0
  110. package/test/sync.js +41 -4
  111. package/benchmark/atoi_cc.cc +0 -59
  112. package/build/qemu/1.2.4/koffi_darwin_arm64.tar.gz +0 -0
  113. package/build/qemu/1.2.4/koffi_darwin_x64.tar.gz +0 -0
  114. package/build/qemu/1.2.4/koffi_freebsd_arm64.tar.gz +0 -0
  115. package/build/qemu/1.2.4/koffi_freebsd_ia32.tar.gz +0 -0
  116. package/build/qemu/1.2.4/koffi_freebsd_x64.tar.gz +0 -0
  117. package/build/qemu/1.2.4/koffi_linux_arm.tar.gz +0 -0
  118. package/build/qemu/1.2.4/koffi_linux_arm64.tar.gz +0 -0
  119. package/build/qemu/1.2.4/koffi_linux_ia32.tar.gz +0 -0
  120. package/build/qemu/1.2.4/koffi_linux_riscv64.tar.gz +0 -0
  121. package/build/qemu/1.2.4/koffi_linux_x64.tar.gz +0 -0
  122. package/build/qemu/1.2.4/koffi_openbsd_ia32.tar.gz +0 -0
  123. package/build/qemu/1.2.4/koffi_openbsd_x64.tar.gz +0 -0
  124. package/build/qemu/1.2.4/koffi_win32_arm64.tar.gz +0 -0
  125. package/build/qemu/1.2.4/koffi_win32_ia32.tar.gz +0 -0
  126. package/build/qemu/1.2.4/koffi_win32_x64.tar.gz +0 -0
package/src/call.hh CHANGED
@@ -28,8 +28,8 @@ struct BackRegisters;
28
28
  // I'm not sure why the alignas(8), because alignof(CallData) is 8 without it.
29
29
  // But on Windows i386, without it, the alignment may not be correct (compiler bug?).
30
30
  class alignas(8) CallData {
31
- struct OutObject {
32
- Napi::ObjectReference ref;
31
+ struct OutArgument {
32
+ napi_ref ref;
33
33
  const uint8_t *ptr;
34
34
  const TypeInfo *type;
35
35
  };
@@ -44,7 +44,7 @@ class alignas(8) CallData {
44
44
 
45
45
  uint32_t used_trampolines = 0;
46
46
 
47
- LocalArray<OutObject, MaxOutParameters> out_objects;
47
+ LocalArray<OutArgument, MaxOutParameters> out_arguments;
48
48
 
49
49
  uint8_t *new_sp;
50
50
  uint8_t *old_sp;
@@ -79,15 +79,22 @@ private:
79
79
  template <typename T = uint8_t>
80
80
  T *AllocHeap(Size size, Size align);
81
81
 
82
- const char *PushString(const Napi::Value &value);
83
- const char16_t *PushString16(const Napi::Value &value);
84
- bool PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t *origin, int16_t realign = 0);
85
- bool PushArray(const Napi::Value &obj, const TypeInfo *type, uint8_t *origin, int16_t realign = 0);
82
+ const char *PushString(Napi::Value value);
83
+ const char16_t *PushString16(Napi::Value value);
84
+ bool PushObject(Napi::Object obj, const TypeInfo *type, uint8_t *origin, int16_t realign = 0);
85
+ bool PushNormalArray(Napi::Array array, Size len, const TypeInfo *ref, uint8_t *origin, int16_t realign = 0);
86
+ bool PushTypedArray(Napi::TypedArray array, Size len, const TypeInfo *ref, uint8_t *origin, int16_t realign = 0);
87
+ bool PushStringArray(Napi::Value value, const TypeInfo *type, uint8_t *origin);
88
+ bool PushPointer(Napi::Value value, const ParameterInfo &param, void **out_ptr);
86
89
 
87
90
  void PopObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type, int16_t realign = 0);
88
91
  Napi::Object PopObject(const uint8_t *origin, const TypeInfo *type, int16_t realign = 0);
92
+ void PopNormalArray(Napi::Array array, const uint8_t *origin, const TypeInfo *ref, int16_t realign = 0);
93
+ void PopTypedArray(Napi::TypedArray array, const uint8_t *origin, const TypeInfo *ref, int16_t realign = 0);
89
94
  Napi::Value PopArray(const uint8_t *origin, const TypeInfo *type, int16_t realign = 0);
90
95
 
96
+ void PopOutArguments();
97
+
91
98
  void *ReserveTrampoline(const FunctionInfo *proto, Napi::Function func);
92
99
  };
93
100
 
package/src/ffi.cc CHANGED
@@ -168,6 +168,8 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
168
168
  type->primitive = PrimitiveKind::Record;
169
169
  type->align = 1;
170
170
 
171
+ HashSet<const char *> members;
172
+
171
173
  for (uint32_t i = 0; i < keys.Length(); i++) {
172
174
  RecordMember member = {};
173
175
 
@@ -188,6 +190,11 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
188
190
  type->size = (int16_t)(AlignLen(type->size, member.align) + member.type->size);
189
191
  type->align = std::max(type->align, member.align);
190
192
 
193
+ if (!members.TrySet(member.name).second) {
194
+ ThrowError<Napi::Error>(env, "Duplicate member '%1' in struct '%2'", member.name, type->name);
195
+ return env.Null();
196
+ }
197
+
191
198
  type->members.Append(member);
192
199
  }
193
200
 
@@ -314,10 +321,6 @@ static Napi::Value EncodePointerDirection(const Napi::CallbackInfo &info, int di
314
321
  ThrowError<Napi::TypeError>(env, "Unexpected %1 type, expected pointer type", PrimitiveKindNames[(int)type->primitive]);
315
322
  return env.Null();
316
323
  }
317
- if ((directions & 2) && type->ref->primitive != PrimitiveKind::Record) {
318
- ThrowError<Napi::TypeError>(env, "Only objects can be used as out parameters (for now)");
319
- return env.Null();
320
- }
321
324
 
322
325
  // We need to lose the const for Napi::External to work
323
326
  TypeInfo *marked = (TypeInfo *)((uint8_t *)type + directions - 1);
@@ -357,6 +360,20 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
357
360
  return env.Null();
358
361
  }
359
362
 
363
+ const TypeInfo *ref = ResolveType(instance, info[0]);
364
+ int64_t len = (uint16_t)info[1].As<Napi::Number>().Int64Value();
365
+
366
+ if (!ref)
367
+ return env.Null();
368
+ if (len <= 0) {
369
+ ThrowError<Napi::TypeError>(env, "Array length must be positive and non-zero");
370
+ return env.Null();
371
+ }
372
+ if (len > INT16_MAX / ref->size) {
373
+ ThrowError<Napi::TypeError>(env, "Array length is too high (max = %1)", INT16_MAX / ref->size);
374
+ return env.Null();
375
+ }
376
+
360
377
  TypeInfo::ArrayHint hint;
361
378
  if (info.Length() >= 3 && !IsNullOrUndefined(info[2])) {
362
379
  if (!info[2].IsString()) {
@@ -371,29 +388,23 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
371
388
  } else if (to == "array") {
372
389
  hint = TypeInfo::ArrayHint::Array;
373
390
  } else if (to == "string") {
391
+ if (ref->primitive != PrimitiveKind::Int8 && ref->primitive != PrimitiveKind::Int16) {
392
+ ThrowError<Napi::Error>(env, "Array hint 'string' can only be used with 8 and 16-bit signed integer types");
393
+ return env.Null();
394
+ }
395
+
374
396
  hint = TypeInfo::ArrayHint::String;
375
397
  } else {
376
- ThrowError<Napi::Error>(env, "Array conversion hint must be 'typed' or 'array'");
398
+ ThrowError<Napi::Error>(env, "Array conversion hint must be 'typed', 'array' or 'string'");
377
399
  return env.Null();
378
400
  }
401
+ } else if (TestStr(ref->name, "char") || TestStr(ref->name, "char16") ||
402
+ TestStr(ref->name, "char16_t")) {
403
+ hint = TypeInfo::ArrayHint::String;
379
404
  } else {
380
405
  hint = TypeInfo::ArrayHint::TypedArray;
381
406
  }
382
407
 
383
- const TypeInfo *ref = ResolveType(instance, info[0]);
384
- int64_t len = (uint16_t)info[1].As<Napi::Number>().Int64Value();
385
-
386
- if (!ref)
387
- return env.Null();
388
- if (len <= 0) {
389
- ThrowError<Napi::TypeError>(env, "Array length must be positive and non-zero");
390
- return env.Null();
391
- }
392
- if (len > INT16_MAX / ref->size) {
393
- ThrowError<Napi::TypeError>(env, "Array length is too high (max = %1)", INT16_MAX / ref->size);
394
- return env.Null();
395
- }
396
-
397
408
  TypeInfo *type = instance->types.AppendDefault();
398
409
 
399
410
  type->name = Fmt(&instance->str_alloc, "%1[%2]", ref->name, len).ptr;
@@ -1011,6 +1022,16 @@ static void RegisterPrimitiveType(InstanceData *instance, const char *name, Prim
1011
1022
  instance->types_map.Set(type);
1012
1023
  }
1013
1024
 
1025
+ static inline PrimitiveKind GetLongPrimitive(bool sign)
1026
+ {
1027
+ switch (RG_SIZE(long)) {
1028
+ case 4: return sign ? PrimitiveKind::Int32 : PrimitiveKind::UInt32;
1029
+ case 8: return sign ? PrimitiveKind::Int64 : PrimitiveKind::UInt64;
1030
+ }
1031
+
1032
+ RG_UNREACHABLE();
1033
+ }
1034
+
1014
1035
  static Napi::Object InitBaseTypes(Napi::Env env)
1015
1036
  {
1016
1037
  InstanceData *instance = env.GetInstanceData<InstanceData>();
@@ -1047,13 +1068,13 @@ static Napi::Object InitBaseTypes(Napi::Env env)
1047
1068
  RegisterPrimitiveType(instance, "int64_t", PrimitiveKind::Int64, 8, alignof(int64_t));
1048
1069
  RegisterPrimitiveType(instance, "uint64", PrimitiveKind::UInt64, 8, alignof(int64_t));
1049
1070
  RegisterPrimitiveType(instance, "uint64_t", PrimitiveKind::UInt64, 8, alignof(int64_t));
1050
- RegisterPrimitiveType(instance, "long", PrimitiveKind::Int64, RG_SIZE(long), alignof(long));
1051
- RegisterPrimitiveType(instance, "ulong", PrimitiveKind::UInt64, RG_SIZE(long), alignof(long));
1052
- RegisterPrimitiveType(instance, "unsigned long", PrimitiveKind::UInt64, RG_SIZE(long), alignof(long));
1053
- RegisterPrimitiveType(instance, "longlong", PrimitiveKind::Int64, RG_SIZE(long long), alignof(long long));
1054
- RegisterPrimitiveType(instance, "long long", PrimitiveKind::Int64, RG_SIZE(long long), alignof(long long));
1055
- RegisterPrimitiveType(instance, "ulonglong", PrimitiveKind::UInt64, RG_SIZE(long long), alignof(long long));
1056
- RegisterPrimitiveType(instance, "unsigned long long", PrimitiveKind::UInt64, RG_SIZE(long long), alignof(long long));
1071
+ RegisterPrimitiveType(instance, "long", GetLongPrimitive(true), RG_SIZE(long), alignof(long));
1072
+ RegisterPrimitiveType(instance, "ulong", GetLongPrimitive(false), RG_SIZE(long), alignof(long));
1073
+ RegisterPrimitiveType(instance, "unsigned long", GetLongPrimitive(false), RG_SIZE(long), alignof(long));
1074
+ RegisterPrimitiveType(instance, "longlong", PrimitiveKind::Int64, RG_SIZE(int64_t), alignof(int64_t));
1075
+ RegisterPrimitiveType(instance, "long long", PrimitiveKind::Int64, RG_SIZE(int64_t), alignof(int64_t));
1076
+ RegisterPrimitiveType(instance, "ulonglong", PrimitiveKind::UInt64, RG_SIZE(uint64_t), alignof(uint64_t));
1077
+ RegisterPrimitiveType(instance, "unsigned long long", PrimitiveKind::UInt64, RG_SIZE(uint64_t), alignof(uint64_t));
1057
1078
  RegisterPrimitiveType(instance, "float32", PrimitiveKind::Float32, 4, alignof(float));
1058
1079
  RegisterPrimitiveType(instance, "float64", PrimitiveKind::Float64, 8, alignof(double));
1059
1080
  RegisterPrimitiveType(instance, "float", PrimitiveKind::Float32, 4, alignof(float));
package/src/ffi.hh CHANGED
@@ -26,7 +26,7 @@ static const Size DefaultAsyncHeapSize = Mebibytes(1);
26
26
  static const int DefaultResidentAsyncPools = 2;
27
27
 
28
28
  static const Size MaxParameters = 32;
29
- static const Size MaxOutParameters = 8;
29
+ static const Size MaxOutParameters = 4;
30
30
  static const Size MaxTrampolines = 16;
31
31
 
32
32
  extern const int TypeInfoMarker;
package/src/parser.cc CHANGED
@@ -70,9 +70,8 @@ bool PrototypeParser::Parse(const char *str, FunctionInfo *out_func)
70
70
  return false;
71
71
  }
72
72
 
73
- if ((param.directions & 2) && (param.type->primitive != PrimitiveKind::Pointer ||
74
- param.type->ref->primitive != PrimitiveKind::Record)) {
75
- MarkError("Only object pointers can be used as out parameters (for now)");
73
+ if ((param.directions & 2) && param.type->primitive != PrimitiveKind::Pointer) {
74
+ MarkError("Only pointers can be used for output parameters");
76
75
  return false;
77
76
  }
78
77
 
@@ -171,23 +170,6 @@ const TypeInfo *PrototypeParser::ParseType()
171
170
  }
172
171
  buf.ptr[--buf.len] = 0;
173
172
 
174
- if (indirect) {
175
- const TypeInfo *type = instance->types_map.FindValue(buf.ptr, nullptr);
176
- PrimitiveKind primitive = type ? type->primitive : PrimitiveKind::Void;
177
-
178
- if (primitive == PrimitiveKind::Int8 || primitive == PrimitiveKind::UInt8) {
179
- buf.RemoveFrom(0);
180
- Fmt(&buf, "string");
181
-
182
- indirect--;
183
- } else if (primitive == PrimitiveKind::Int16 || primitive == PrimitiveKind::UInt16) {
184
- buf.RemoveFrom(0);
185
- Fmt(&buf, "string16");
186
-
187
- indirect--;
188
- }
189
- }
190
-
191
173
  while (buf.len) {
192
174
  const TypeInfo *type = instance->types_map.FindValue(buf.ptr, nullptr);
193
175
 
package/src/util.cc CHANGED
@@ -91,20 +91,41 @@ const char *GetValueType(const InstanceData *instance, Napi::Value value)
91
91
  return type.name;
92
92
  }
93
93
 
94
+ if (value.IsArray()) {
95
+ return "Array";
96
+ } else if (value.IsTypedArray()) {
97
+ Napi::TypedArray array = value.As<Napi::TypedArray>();
98
+
99
+ switch (array.TypedArrayType()) {
100
+ case napi_int8_array: return "Int8Array";
101
+ case napi_uint8_array: return "Uint8Array";
102
+ case napi_uint8_clamped_array: return "Uint8ClampedArray";
103
+ case napi_int16_array: return "Int16Array";
104
+ case napi_uint16_array: return "Uint16Array";
105
+ case napi_int32_array: return "Int32Array";
106
+ case napi_uint32_array: return "Uint32Array";
107
+ case napi_float32_array: return "Float32Array";
108
+ case napi_float64_array: return "Float64Array";
109
+ case napi_bigint64_array: return "BigInt64Array";
110
+ case napi_biguint64_array: return "BigUint64Array";
111
+ }
112
+ }
113
+
94
114
  switch (value.Type()) {
95
- case napi_undefined: return "undefined";
96
- case napi_null: return "null";
97
- case napi_boolean: return "boolean";
98
- case napi_number: return "number";
99
- case napi_string: return "string";
100
- case napi_symbol: return "symbol";
101
- case napi_object: return "object";
102
- case napi_function: return "fucntion";
103
- case napi_external: return "external";
104
- case napi_bigint: return "bigint";
115
+ case napi_undefined: return "Undefined";
116
+ case napi_null: return "Null";
117
+ case napi_boolean: return "Boolean";
118
+ case napi_number: return "Number";
119
+ case napi_string: return "String";
120
+ case napi_symbol: return "Symbol";
121
+ case napi_object: return "Object";
122
+ case napi_function: return "Function";
123
+ case napi_external: return "External";
124
+ case napi_bigint: return "BigInt";
105
125
  }
106
126
 
107
- return "unknown";
127
+ // This should not be possible, but who knows...
128
+ return "Unknown";
108
129
  }
109
130
 
110
131
  void SetValueTag(const InstanceData *instance, Napi::Value value, const void *marker)
@@ -126,6 +147,24 @@ bool CheckValueTag(const InstanceData *instance, Napi::Value value, const void *
126
147
  return match;
127
148
  }
128
149
 
150
+ int GetTypedArrayType(const TypeInfo *type)
151
+ {
152
+ switch (type->primitive) {
153
+ case PrimitiveKind::Int8: return napi_int8_array;
154
+ case PrimitiveKind::UInt8: return napi_uint8_array;
155
+ case PrimitiveKind::Int16: return napi_int16_array;
156
+ case PrimitiveKind::UInt16: return napi_uint16_array;
157
+ case PrimitiveKind::Int32: return napi_int32_array;
158
+ case PrimitiveKind::UInt32: return napi_uint32_array;
159
+ case PrimitiveKind::Float32: return napi_float32_array;
160
+ case PrimitiveKind::Float64: return napi_float64_array;
161
+
162
+ default: return -1;
163
+ }
164
+
165
+ RG_UNREACHABLE();
166
+ }
167
+
129
168
  static int AnalyseFlatRec(const TypeInfo *type, int offset, int count, FunctionRef<void(const TypeInfo *type, int offset, int count)> func)
130
169
  {
131
170
  if (type->primitive == PrimitiveKind::Record) {
package/src/util.hh CHANGED
@@ -86,6 +86,8 @@ static inline bool IsObject(Napi::Value value)
86
86
  return value.IsObject() && !IsNullOrUndefined(value) && !value.IsArray();
87
87
  }
88
88
 
89
+ int GetTypedArrayType(const TypeInfo *type);
90
+
89
91
  template <typename T>
90
92
  T CopyNumber(const Napi::Value &value)
91
93
  {
package/test/misc.c CHANGED
@@ -124,6 +124,11 @@ typedef struct SingleU32 { uint32_t v; } SingleU32;
124
124
  typedef struct SingleU64 { uint64_t v; } SingleU64;
125
125
  typedef struct SingleI64 { int64_t v; } SingleI64;
126
126
 
127
+ typedef struct IntContainer {
128
+ int values[16];
129
+ int len;
130
+ } IntContainer;
131
+
127
132
  EXPORT void FillPack1(int a, Pack1 *p)
128
133
  {
129
134
  p->a = a;
@@ -512,3 +517,29 @@ EXPORT int ApplyStd(int a, int b, int c, ApplyCallback *func)
512
517
  int ret = func(a, b, c);
513
518
  return ret;
514
519
  }
520
+
521
+ EXPORT IntContainer ArrayToStruct(int *values, int len)
522
+ {
523
+ IntContainer ic;
524
+
525
+ memset(&ic, 0, sizeof(ic));
526
+ memcpy(ic.values, values, len * sizeof(*values));
527
+ ic.len = len;
528
+
529
+ return ic;
530
+ }
531
+
532
+ EXPORT void FillRange(int init, int step, int *out, int len)
533
+ {
534
+ while (--len >= 0) {
535
+ *(out++) = init;
536
+ init += step;
537
+ }
538
+ }
539
+
540
+ EXPORT void MultiplyIntegers(int multiplier, int *values, int len)
541
+ {
542
+ for (int i = 0; i < len; i++) {
543
+ values[i] *= multiplier;
544
+ }
545
+ }
package/test/sync.js CHANGED
@@ -84,23 +84,28 @@ const PackedBFG = koffi.pack('PackedBFG', {
84
84
  });
85
85
 
86
86
  const FixedString = koffi.struct('FixedString', {
87
- buf: koffi.array('char', 64)
87
+ buf: koffi.array('int8', 64)
88
88
  });
89
89
  const FixedString2 = koffi.struct('FixedString2', {
90
- buf: koffi.array('char', 64, 'string')
90
+ buf: koffi.array('char', 64)
91
91
  });
92
92
 
93
93
  const FixedWide = koffi.struct('FixedWide', {
94
- buf: koffi.array('char16', 64)
94
+ buf: koffi.array('char16', 64, 'typed')
95
95
  });
96
96
  const FixedWide2 = koffi.struct('FixedWide2', {
97
- buf: koffi.array('char16', 64, 'string')
97
+ buf: koffi.array('char16', 64)
98
98
  });
99
99
 
100
100
  const SingleU32 = koffi.struct('SingleU32', { v: 'uint32_t' });
101
101
  const SingleU64 = koffi.struct('SingleU64', { v: 'uint64_t' });
102
102
  const SingleI64 = koffi.struct('SingleI64', { v: 'int64_t' });
103
103
 
104
+ const IntContainer = koffi.struct('IntContainer', {
105
+ values: koffi.array('int', 16),
106
+ len: 'int'
107
+ });
108
+
104
109
  main();
105
110
 
106
111
  async function main() {
@@ -163,6 +168,9 @@ async function test() {
163
168
  const ThroughInt64SS = lib.func('SingleI64 ThroughInt64SS(SingleI64 s)');
164
169
  const ThroughInt64SI = lib.func('SingleI64 ThroughInt64SI(int64_t v)');
165
170
  const ThroughInt64IS = lib.func('int64_t ThroughInt64IS(SingleI64 s)');
171
+ const ArrayToStruct = lib.func('IntContainer ArrayToStruct(int *ptr, int len)');
172
+ const FillRange = lib.func('void FillRange(int init, int step, _Out_ int *out, int len)');
173
+ const MultiplyIntegers = lib.func('void MultiplyIntegers(int multiplier, _Inout_ int *values, int len)');
166
174
 
167
175
  // Simple tests with Pack1
168
176
  {
@@ -320,4 +328,33 @@ async function test() {
320
328
  assert.deepEqual(ThroughInt64SI(-9223372036854775803n), { v: -9223372036854775803n });
321
329
  assert.equal(ThroughInt64IS({ v: -9223372036854775803n }), -9223372036854775803n);
322
330
  }
331
+
332
+ // Array pointers as input
333
+ {
334
+ let arr1 = [5, 7, 8, 4];
335
+ let arr2 = Int32Array.from([8, 454, 6, 3, 45]);
336
+
337
+ assert.deepEqual(ArrayToStruct(arr1, 0), { values: Int32Array.from(Array(16).fill(0)), len: 0 });
338
+ assert.deepEqual(ArrayToStruct(arr1, 3), { values: Int32Array.from([5, 7, 8, ...Array(13).fill(0)]), len: 3 });
339
+ assert.deepEqual(ArrayToStruct(arr2, 4), { values: Int32Array.from([8, 454, 6, 3, ...Array(12).fill(0)]), len: 4 });
340
+ assert.deepEqual(ArrayToStruct(arr2, 2), { values: Int32Array.from([8, 454, ...Array(14).fill(0)]), len: 2 });
341
+ }
342
+
343
+ // Array pointers as output
344
+ {
345
+ let out1 = Array(10);
346
+ let out2 = new Int32Array(10);
347
+ let mult = -3;
348
+
349
+ FillRange(2, 7, out1, out1.length);
350
+ FillRange(13, 3, out2, out2.length);
351
+
352
+ assert.deepEqual(out1, [2, 9, 16, 23, 30, 37, 44, 51, 58, 65]);
353
+ assert.deepEqual(out2, new Int32Array([13, 16, 19, 22, 25, 28, 31, 34, 37, 40]));
354
+
355
+ MultiplyIntegers(-1, out1, out1.length - 2);
356
+ MultiplyIntegers(3, out2, out2.length - 3);
357
+ assert.deepEqual(out1, [-2, -9, -16, -23, -30, -37, -44, -51, 58, 65]);
358
+ assert.deepEqual(out2, new Int32Array([3 * 13, 3 * 16, 3 * 19, 3 * 22, 3 * 25, 3 * 28, 3 * 31, 34, 37, 40]));
359
+ }
323
360
  }
@@ -1,59 +0,0 @@
1
- // This program is free software: you can redistribute it and/or modify
2
- // it under the terms of the GNU Affero General Public License as published by
3
- // the Free Software Foundation, either version 3 of the License, or
4
- // (at your option) any later version.
5
- //
6
- // This program is distributed in the hope that it will be useful,
7
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
8
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
- // GNU Affero General Public License for more details.
10
- //
11
- // You should have received a copy of the GNU Affero General Public License
12
- // along with this program. If not, see https://www.gnu.org/licenses/.
13
-
14
- #include "../vendor/libcc/libcc.hh"
15
-
16
- namespace RG {
17
-
18
- static const char * strings[] = {
19
- "424242",
20
- "foobar",
21
- "123456789",
22
- };
23
-
24
- volatile uint64_t sum = 0;
25
-
26
- int Main(int argc, char **argv)
27
- {
28
- int iterations = 20000000;
29
-
30
- if (argc >= 2) {
31
- if (!ParseInt(argv[1], &iterations))
32
- return 1;
33
- }
34
- LogInfo("Iterations: %1", iterations);
35
-
36
- int64_t start = GetMonotonicTime();
37
-
38
- for (int i = 0; i < iterations; i++) {
39
- sum = sum + (uint64_t)atoi(strings[i % RG_LEN(strings)]);
40
- }
41
-
42
- // Help prevent optimisation of loop
43
- {
44
- PushLogFilter([](LogLevel, const char *, const char *, FunctionRef<LogFunc>) {});
45
- RG_DEFER { PopLogFilter(); };
46
-
47
- LogInfo("Sum = %1", sum);
48
- }
49
-
50
- int64_t time = GetMonotonicTime() - start;
51
- LogInfo("Time: %1s", FmtDouble((double)time / 1000.0, 2));
52
-
53
- return 0;
54
- }
55
-
56
- }
57
-
58
- // C++ namespaces are stupid
59
- int main(int argc, char **argv) { return RG::Main(argc, argv); }