koffi 1.1.0-beta.5 → 1.1.0-beta.8

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/CMakeLists.txt CHANGED
@@ -65,9 +65,6 @@ if(WIN32)
65
65
  target_compile_definitions(koffi PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
66
66
  target_link_libraries(koffi PRIVATE ws2_32)
67
67
  endif()
68
- if(MSVC)
69
- target_compile_options(koffi PRIVATE /wd4200)
70
- else()
71
- target_compile_options(koffi PRIVATE -fno-exceptions -Wno-missing-field-initializers
72
- -fno-strict-aliasing)
68
+ if(NOT MSVC)
69
+ target_compile_options(koffi PRIVATE -fno-exceptions -fno-strict-aliasing)
73
70
  endif()
package/README.md CHANGED
@@ -231,6 +231,33 @@ Koffi exposes three functions to explore type information:
231
231
  Fixed-size arrays are declared with `koffi.array(type, length)`. Just like in C, they cannot be passed
232
232
  as functions parameters (they degenerate to pointers), or returned by value. You can however embed them in struct types.
233
233
 
234
+ Special rules apply for arrays of primitive integer and float types (uint32_t, double, etc...):
235
+ - When converting from JS to C, Koffi can take a normal Array (e.g. `[1, 2]`) or a TypedArray of the correct type (e.g. `Uint8Array` for an array of `uint8_t` numbers)
236
+ - When converting from C to JS (for return value or output parameters), Koffi will by default use a TypedArray. But you can change this behavior when you create the array type with the optional hint argument: `koffi.array('uint8_t', 64, 'array')`
237
+
238
+ See the example below:
239
+
240
+ ```js
241
+ const koffi = require('koffi');
242
+
243
+ // Those two structs are exactly the same, only the array conversion hint is different
244
+ const Foo1 = koffi.struct('Foo', {
245
+ i: 'int',
246
+ a16: koffi.array('int16_t', 8)
247
+ });
248
+ const Foo2 = koffi.struct('Foo', {
249
+ i: 'int',
250
+ a16: koffi.array('int16_t', 8, 'array')
251
+ });
252
+
253
+ // Uses an hypothetical C function that just returns the struct passed as a parameter
254
+ const ReturnFoo1 = lib.func('Foo1 ReturnFoo(Foo1 p)');
255
+ const ReturnFoo2 = lib.func('Foo2 ReturnFoo(Foo2 p)');
256
+
257
+ console.log(ReturnFoo1({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: Int16Array(2) [6, 8] }
258
+ console.log(ReturnFoo2({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: [6, 8] }
259
+ ```
260
+
234
261
  ## Variadic functions
235
262
 
236
263
  Variadic functions are declared with an ellipsis as the last argument.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "1.1.0-beta.5",
3
+ "version": "1.1.0-beta.8",
4
4
  "description": "Fast and simple FFI (foreign function interface) for Node.js",
5
5
  "keywords": [
6
6
  "foreign",
package/src/call.cc CHANGED
@@ -20,8 +20,8 @@
20
20
 
21
21
  namespace RG {
22
22
 
23
- CallData::CallData(Napi::Env env, const FunctionInfo *func, InstanceMemory *mem, bool debug)
24
- : env(env), instance(env.GetInstanceData<InstanceData>()), func(func), debug(debug),
23
+ CallData::CallData(Napi::Env env, InstanceData *instance, const FunctionInfo *func, InstanceMemory *mem)
24
+ : env(env), instance(instance), func(func), debug(instance->debug),
25
25
  mem(mem), old_stack_mem(mem->stack), old_heap_mem(mem->heap)
26
26
  {
27
27
  mem->depth++;
@@ -67,7 +67,7 @@ const char *CallData::PushString(const Napi::Value &value)
67
67
 
68
68
  len++;
69
69
 
70
- buf.ptr = (char *)Allocator::Allocate(&mem->big_alloc, (Size)len);
70
+ buf.ptr = (char *)Allocator::Allocate(&call_alloc, (Size)len);
71
71
  buf.len = (Size)len;
72
72
 
73
73
  status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
@@ -104,7 +104,7 @@ const char16_t *CallData::PushString16(const Napi::Value &value)
104
104
 
105
105
  len++;
106
106
 
107
- buf.ptr = (char16_t *)Allocator::Allocate(&mem->big_alloc, (Size)len * 2);
107
+ buf.ptr = (char16_t *)Allocator::Allocate(&call_alloc, (Size)len * 2);
108
108
  buf.len = (Size)len;
109
109
 
110
110
  status = napi_get_value_string_utf16(env, value, buf.ptr, (size_t)buf.len, &len);
@@ -246,31 +246,38 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
246
246
  if (obj.IsArray()) {
247
247
  Napi::Array array = obj.As<Napi::Array>();
248
248
 
249
- if (array.Length() != len) {
249
+ if (RG_UNLIKELY(array.Length() != len)) {
250
250
  ThrowError<Napi::Error>(env, "Expected array of length %1, got %2", len, array.Length());
251
251
  return false;
252
252
  }
253
253
 
254
+ #define PUSH_ARRAY(Check, Expected, GetCode) \
255
+ do { \
256
+ for (uint32_t i = 0; i < len; i++) { \
257
+ Napi::Value value = array[i]; \
258
+ \
259
+ int16_t align = std::max(type->ref->align, realign); \
260
+ dest = AlignUp(dest, align); \
261
+ \
262
+ if (RG_UNLIKELY(!(Check))) { \
263
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected %2", GetValueType(instance, value), (Expected)); \
264
+ return false; \
265
+ } \
266
+ \
267
+ GetCode \
268
+ \
269
+ dest += type->ref->size; \
270
+ } \
271
+ } while (false)
272
+
254
273
  switch (type->ref->primitive) {
255
274
  case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
256
275
 
257
276
  case PrimitiveKind::Bool: {
258
- for (uint32_t i = 0; i < len; i++) {
259
- Napi::Value value = array[i];
260
-
261
- int16_t align = std::max(type->ref->align, realign);
262
- dest = AlignUp(dest, align);
263
-
264
- if (RG_UNLIKELY(!value.IsBoolean())) {
265
- ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected boolean", GetValueType(instance, value));
266
- return false;
267
- }
268
-
277
+ PUSH_ARRAY(value.IsBoolean(), "boolean", {
269
278
  bool b = value.As<Napi::Boolean>();
270
279
  *(bool *)dest = b;
271
-
272
- dest += type->ref->size;
273
- }
280
+ });
274
281
  } break;
275
282
  case PrimitiveKind::Int8:
276
283
  case PrimitiveKind::UInt8:
@@ -280,161 +287,67 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
280
287
  case PrimitiveKind::UInt32:
281
288
  case PrimitiveKind::Int64:
282
289
  case PrimitiveKind::UInt64: {
283
- for (uint32_t i = 0; i < len; i++) {
284
- Napi::Value value = array[i];
285
-
286
- int16_t align = std::max(type->ref->align, realign);
287
- dest = AlignUp(dest, align);
288
-
289
- if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
290
- ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected number", GetValueType(instance, value));
291
- return false;
292
- }
293
-
290
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
294
291
  int64_t v = CopyNumber<int64_t>(value);
295
292
  memcpy(dest, &v, type->ref->size); // Little Endian
296
-
297
- dest += type->ref->size;
298
- }
293
+ });
299
294
  } break;
300
295
  case PrimitiveKind::String: {
301
- for (uint32_t i = 0; i < len; i++) {
302
- Napi::Value value = array[i];
303
-
304
- int16_t align = std::max(type->ref->align, realign);
305
- dest = AlignUp(dest, align);
306
-
307
- if (RG_UNLIKELY(!value.IsString())) {
308
- ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected string", GetValueType(instance, value));
309
- return false;
310
- }
311
-
296
+ PUSH_ARRAY(value.IsString(), "string", {
312
297
  const char *str = PushString(value);
313
298
  if (RG_UNLIKELY(!str))
314
299
  return false;
315
300
  *(const char **)dest = str;
316
-
317
- dest += type->ref->size;
318
- }
301
+ });
319
302
  } break;
320
303
  case PrimitiveKind::String16: {
321
- for (uint32_t i = 0; i < len; i++) {
322
- Napi::Value value = array[i];
323
-
324
- int16_t align = std::max(type->ref->align, realign);
325
- dest = AlignUp(dest, align);
326
-
327
- if (RG_UNLIKELY(!value.IsString())) {
328
- ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected string", GetValueType(instance, value));
329
- return false;
330
- }
331
-
304
+ PUSH_ARRAY(value.IsString(), "string", {
332
305
  const char16_t *str16 = PushString16(value);
333
306
  if (RG_UNLIKELY(!str16))
334
307
  return false;
335
308
  *(const char16_t **)dest = str16;
336
-
337
- dest += type->ref->size;
338
- }
309
+ });
339
310
  } break;
340
311
  case PrimitiveKind::Pointer: {
341
- for (uint32_t i = 0; i < len; i++) {
342
- Napi::Value value = array[i];
343
-
344
- int16_t align = std::max(type->ref->align, realign);
345
- dest = AlignUp(dest, align);
346
-
347
- if (RG_UNLIKELY(!CheckValueTag(instance, value, type->ref))) {
348
- ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected %2", GetValueType(instance, value), type->ref->name);
349
- return false;
350
- }
351
-
312
+ PUSH_ARRAY(CheckValueTag(instance, value, type->ref), type->ref->name, {
352
313
  Napi::External external = value.As<Napi::External<void>>();
353
314
  *(void **)dest = external.Data();
354
-
355
- dest += type->ref->size;
356
- }
315
+ });
357
316
  } break;
358
317
  case PrimitiveKind::Record: {
359
- for (uint32_t i = 0; i < len; i++) {
360
- Napi::Value value = array[i];
361
-
362
- int16_t align = std::max(type->ref->align, realign);
363
- dest = AlignUp(dest, align);
364
-
365
- if (RG_UNLIKELY(!IsObject(value))) {
366
- ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected object", GetValueType(instance, value));
367
- return false;
368
- }
369
-
318
+ PUSH_ARRAY(IsObject(value), "object", {
370
319
  Napi::Object obj = value.As<Napi::Object>();
371
320
  if (!PushObject(obj, type->ref, dest, realign))
372
321
  return false;
373
-
374
- dest += type->ref->size;
375
- }
322
+ });
376
323
  } break;
377
324
  case PrimitiveKind::Array: {
378
- for (uint32_t i = 0; i < len; i++) {
379
- Napi::Value value = array[i];
380
-
381
- int16_t align = std::max(type->ref->align, realign);
382
- dest = AlignUp(dest, align);
383
-
384
- if (RG_UNLIKELY(!value.IsArray() && !value.IsTypedArray())) {
385
- ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected array", GetValueType(instance, value));
386
- return false;
387
- }
388
-
389
- Napi::Array array = value.As<Napi::Array>();
325
+ PUSH_ARRAY(value.IsArray() || value.IsTypedArray(), "array", {
326
+ Napi::Object array = value.As<Napi::Array>();
390
327
  if (!PushArray(array, type->ref, dest, realign))
391
328
  return false;
392
-
393
- dest += type->ref->size;
394
- }
329
+ });
395
330
  } break;
396
331
  case PrimitiveKind::Float32: {
397
- for (uint32_t i = 0; i < len; i++) {
398
- Napi::Value value = array[i];
399
-
400
- int16_t align = std::max(type->ref->align, realign);
401
- dest = AlignUp(dest, align);
402
-
403
- if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
404
- ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected number", GetValueType(instance, value));
405
- return false;
406
- }
407
-
332
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
408
333
  float f = CopyNumber<float>(value);
409
334
  *(float *)dest = f;
410
-
411
- dest += type->ref->size;
412
- }
335
+ });
413
336
  } break;
414
337
  case PrimitiveKind::Float64: {
415
- for (uint32_t i = 0; i < len; i++) {
416
- Napi::Value value = array[i];
417
-
418
- int16_t align = std::max(type->ref->align, realign);
419
- dest = AlignUp(dest, align);
420
-
421
- if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
422
- ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected number", GetValueType(instance, value));
423
- return false;
424
- }
425
-
338
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
426
339
  double d = CopyNumber<double>(value);
427
340
  *(double *)dest = d;
428
-
429
- dest += type->ref->size;
430
- }
341
+ });
431
342
  } break;
432
343
  }
344
+
345
+ #undef PUSH_ARRAY
433
346
  } else {
434
347
  Napi::TypedArray array = obj.As<Napi::TypedArray>();
435
348
  const uint8_t *buf = (const uint8_t *)array.ArrayBuffer().Data();
436
349
 
437
- if (array.ElementLength() != len) {
350
+ if (RG_UNLIKELY(array.ElementLength() != len)) {
438
351
  ThrowError<Napi::Error>(env, "Expected array of length %1, got %2", len, array.ElementLength());
439
352
  return false;
440
353
  }
@@ -452,7 +365,7 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
452
365
 
453
366
  default: { match = false; } break;
454
367
  }
455
- if (!match) {
368
+ if (RG_UNLIKELY(!match)) {
456
369
  ThrowError<Napi::TypeError>(env, "TypedArray is not approriate for %1 array", type->ref->name);
457
370
  return false;
458
371
  }
@@ -570,256 +483,127 @@ Napi::Object CallData::PopArray(const uint8_t *src, const TypeInfo *type, int16_
570
483
 
571
484
  uint32_t len = type->size / type->ref->size;
572
485
 
486
+ #define POP_ARRAY(SetCode) \
487
+ do { \
488
+ Napi::Array array = Napi::Array::New(env); \
489
+ \
490
+ for (uint32_t i = 0; i < len; i++) { \
491
+ int16_t align = std::max(realign, type->ref->align); \
492
+ src = AlignUp(src, align); \
493
+ \
494
+ SetCode \
495
+ \
496
+ src += type->ref->size; \
497
+ } \
498
+ \
499
+ return array; \
500
+ } while (false)
501
+ #define POP_NUMBER_ARRAY(TypedArrayType, CType) \
502
+ do { \
503
+ if (type->hint == TypeInfo::ArrayHint::TypedArray) { \
504
+ Napi::TypedArrayType array = Napi::TypedArrayType::New(env, len); \
505
+ \
506
+ for (uint32_t i = 0; i < len; i++) { \
507
+ int16_t align = std::max(realign, type->ref->align); \
508
+ src = AlignUp(src, align); \
509
+ \
510
+ CType f = *(CType *)src; \
511
+ array[i] = f; \
512
+ \
513
+ src += type->ref->size; \
514
+ } \
515
+ \
516
+ return array; \
517
+ } else { \
518
+ POP_ARRAY({ \
519
+ double d = (double)*(CType *)src; \
520
+ array.Set(i, Napi::Number::New(env, d)); \
521
+ }); \
522
+ } \
523
+ } while (false)
524
+
573
525
  switch (type->ref->primitive) {
574
526
  case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
575
527
 
576
528
  case PrimitiveKind::Bool: {
577
- Napi::Array array = Napi::Array::New(env);
578
-
579
- for (uint32_t i = 0; i < len; i++) {
580
- int16_t align = std::max(realign, type->ref->align);
581
- src = AlignUp(src, align);
582
-
529
+ POP_ARRAY({
583
530
  bool b = *(bool *)src;
584
531
  array.Set(i, Napi::Boolean::New(env, b));
585
-
586
- src += type->ref->size;
587
- }
588
-
589
- return array;
590
- } break;
591
- case PrimitiveKind::Int8: {
592
- Napi::Int8Array array = Napi::Int8Array::New(env, len);
593
-
594
- for (uint32_t i = 0; i < len; i++) {
595
- int16_t align = std::max(realign, type->ref->align);
596
- src = AlignUp(src, align);
597
-
598
- int8_t v = *(int8_t *)src;
599
- array[i] = v;
600
-
601
- src += type->ref->size;
602
- }
603
-
604
- return array;
605
- } break;
606
- case PrimitiveKind::UInt8: {
607
- Napi::Uint8Array array = Napi::Uint8Array::New(env, len);
608
-
609
- for (uint32_t i = 0; i < len; i++) {
610
- int16_t align = std::max(realign, type->ref->align);
611
- src = AlignUp(src, align);
612
-
613
- uint8_t v = *(uint8_t *)src;
614
- array[i] = v;
615
-
616
- src += type->ref->size;
617
- }
618
-
619
- return array;
620
- } break;
621
- case PrimitiveKind::Int16: {
622
- Napi::Int16Array array = Napi::Int16Array::New(env, len);
623
-
624
- for (uint32_t i = 0; i < len; i++) {
625
- int16_t align = std::max(realign, type->ref->align);
626
- src = AlignUp(src, align);
627
-
628
- int16_t v = *(int16_t *)src;
629
- array[i] = v;
630
-
631
- src += type->ref->size;
632
- }
633
-
634
- return array;
635
- } break;
636
- case PrimitiveKind::UInt16: {
637
- Napi::Uint16Array array = Napi::Uint16Array::New(env, len);
638
-
639
- for (uint32_t i = 0; i < len; i++) {
640
- int16_t align = std::max(realign, type->ref->align);
641
- src = AlignUp(src, align);
642
-
643
- uint16_t v = *(uint16_t *)src;
644
- array[i] = v;
645
-
646
- src += type->ref->size;
647
- }
648
-
649
- return array;
650
- } break;
651
- case PrimitiveKind::Int32: {
652
- Napi::Int32Array array = Napi::Int32Array::New(env, len);
653
-
654
- for (uint32_t i = 0; i < len; i++) {
655
- int16_t align = std::max(realign, type->ref->align);
656
- src = AlignUp(src, align);
657
-
658
- int32_t v = *(int32_t *)src;
659
- array[i] = v;
660
-
661
- src += type->ref->size;
662
- }
663
-
664
- return array;
665
- } break;
666
- case PrimitiveKind::UInt32: {
667
- Napi::Uint32Array array = Napi::Uint32Array::New(env, len);
668
-
669
- for (uint32_t i = 0; i < len; i++) {
670
- int16_t align = std::max(realign, type->ref->align);
671
- src = AlignUp(src, align);
672
-
673
- uint32_t v = *(uint32_t *)src;
674
- array[i] = v;
675
-
676
- src += type->ref->size;
677
- }
678
-
679
- return array;
532
+ });
680
533
  } break;
534
+ case PrimitiveKind::Int8: { POP_NUMBER_ARRAY(Int8Array, int8_t); } break;
535
+ case PrimitiveKind::UInt8: { POP_NUMBER_ARRAY(Uint8Array, uint8_t); } break;
536
+ case PrimitiveKind::Int16: { POP_NUMBER_ARRAY(Int16Array, int16_t); } break;
537
+ case PrimitiveKind::UInt16: { POP_NUMBER_ARRAY(Uint16Array, uint16_t); } break;
538
+ case PrimitiveKind::Int32: { POP_NUMBER_ARRAY(Int32Array, int32_t); } break;
539
+ case PrimitiveKind::UInt32: { POP_NUMBER_ARRAY(Uint32Array, uint32_t); } break;
681
540
  case PrimitiveKind::Int64: {
682
- Napi::Array array = Napi::Array::New(env);
683
-
684
- for (uint32_t i = 0; i < len; i++) {
685
- int16_t align = std::max(realign, type->ref->align);
686
- src = AlignUp(src, align);
687
-
541
+ POP_ARRAY({
688
542
  int64_t v = *(int64_t *)src;
689
543
  array.Set(i, Napi::BigInt::New(env, v));
690
-
691
- src += type->ref->size;
692
- }
693
-
694
- return array;
544
+ });
695
545
  } break;
696
546
  case PrimitiveKind::UInt64: {
697
- Napi::Array array = Napi::Array::New(env);
698
-
699
- for (uint32_t i = 0; i < len; i++) {
700
- int16_t align = std::max(realign, type->ref->align);
701
- src = AlignUp(src, align);
702
-
547
+ POP_ARRAY({
703
548
  uint64_t v = *(uint64_t *)src;
704
549
  array.Set(i, Napi::BigInt::New(env, v));
705
-
706
- src += type->ref->size;
707
- }
708
-
709
- return array;
550
+ });
710
551
  } break;
711
552
  case PrimitiveKind::String: {
712
- Napi::Array array = Napi::Array::New(env);
713
-
714
- for (uint32_t i = 0; i < len; i++) {
715
- int16_t align = std::max(realign, type->ref->align);
716
- src = AlignUp(src, align);
717
-
553
+ POP_ARRAY({
718
554
  const char *str = *(const char **)src;
719
555
  array.Set(i, Napi::String::New(env, str));
720
-
721
- src += type->ref->size;
722
- }
723
-
724
- return array;
556
+ });
725
557
  } break;
726
558
  case PrimitiveKind::String16: {
727
- Napi::Array array = Napi::Array::New(env);
728
-
729
- for (uint32_t i = 0; i < len; i++) {
730
- int16_t align = std::max(realign, type->ref->align);
731
- src = AlignUp(src, align);
732
-
559
+ POP_ARRAY({
733
560
  const char16_t *str16 = *(const char16_t **)src;
734
561
  array.Set(i, Napi::String::New(env, str16));
735
-
736
- src += type->ref->size;
737
- }
738
-
739
- return array;
562
+ });
740
563
  } break;
741
564
  case PrimitiveKind::Pointer: {
742
- Napi::Array array = Napi::Array::New(env);
743
-
744
- for (uint32_t i = 0; i < len; i++) {
745
- int16_t align = std::max(realign, type->ref->align);
746
- src = AlignUp(src, align);
747
-
565
+ POP_ARRAY({
748
566
  void *ptr2 = *(void **)src;
749
567
 
750
568
  Napi::External<void> external = Napi::External<void>::New(env, ptr2);
751
569
  SetValueTag(instance, external, type->ref);
752
570
 
753
571
  array.Set(i, external);
754
-
755
- src += type->ref->size;
756
- }
757
-
758
- return array;
572
+ });
759
573
  } break;
760
574
  case PrimitiveKind::Record: {
761
- Napi::Array array = Napi::Array::New(env);
762
-
763
- for (uint32_t i = 0; i < len; i++) {
764
- int16_t align = std::max(realign, type->ref->align);
765
- src = AlignUp(src, align);
766
-
575
+ POP_ARRAY({
767
576
  Napi::Object obj = PopObject(src, type->ref, realign);
768
577
  array.Set(i, obj);
769
-
770
- src += type->ref->size;
771
- }
772
-
773
- return array;
578
+ });
774
579
  } break;
775
580
  case PrimitiveKind::Array: {
776
- Napi::Array array = Napi::Array::New(env);
777
-
778
- for (uint32_t i = 0; i < len; i++) {
779
- int16_t align = std::max(realign, type->ref->align);
780
- src = AlignUp(src, align);
781
-
581
+ POP_ARRAY({
782
582
  Napi::Object obj = PopArray(src, type->ref, realign);
783
583
  array.Set(i, obj);
784
-
785
- src += type->ref->size;
786
- }
787
-
788
- return array;
789
- } break;
790
- case PrimitiveKind::Float32: {
791
- Napi::Float32Array array = Napi::Float32Array::New(env, len);
792
-
793
- for (uint32_t i = 0; i < len; i++) {
794
- int16_t align = std::max(realign, type->ref->align);
795
- src = AlignUp(src, align);
796
-
797
- float f = *(float *)src;
798
- array[i] = f;
799
-
800
- src += type->ref->size;
801
- }
802
-
803
- return array;
584
+ });
804
585
  } break;
805
- case PrimitiveKind::Float64: {
806
- Napi::Float64Array array = Napi::Float64Array::New(env, len);
586
+ case PrimitiveKind::Float32: { POP_NUMBER_ARRAY(Float32Array, float); } break;
587
+ case PrimitiveKind::Float64: { POP_NUMBER_ARRAY(Float64Array, double); } break;
588
+ }
807
589
 
808
- for (uint32_t i = 0; i < len; i++) {
809
- int16_t align = std::max(realign, type->ref->align);
810
- src = AlignUp(src, align);
590
+ #undef POP_NUMBER_ARRAY
591
+ #undef POP_ARRAY
811
592
 
812
- double d = *(double *)src;
813
- array[i] = d;
593
+ RG_UNREACHABLE();
594
+ }
814
595
 
815
- src += type->ref->size;
816
- }
596
+ Napi::Value CallData::Run(const Napi::CallbackInfo &info)
597
+ {
598
+ if (!RG_UNLIKELY(Prepare(info)))
599
+ return env.Null();
817
600
 
818
- return array;
819
- } break;
601
+ if (debug) {
602
+ DumpDebug();
820
603
  }
604
+ Execute();
821
605
 
822
- RG_UNREACHABLE();
606
+ return Complete();
823
607
  }
824
608
 
825
609
  static void DumpMemory(const char *type, Span<const uint8_t> bytes)
package/src/call.hh CHANGED
@@ -55,26 +55,17 @@ class CallData {
55
55
  } result;
56
56
  uint8_t *return_ptr = nullptr;
57
57
 
58
+ LinkedAllocator call_alloc;
59
+
58
60
  public:
59
- CallData(Napi::Env env, const FunctionInfo *func, InstanceMemory *mem, bool debug);
61
+ CallData(Napi::Env env, InstanceData *instance, const FunctionInfo *func, InstanceMemory *mem);
60
62
  ~CallData();
61
63
 
62
64
  bool Prepare(const Napi::CallbackInfo &info);
63
65
  void Execute();
64
66
  Napi::Value Complete();
65
67
 
66
- Napi::Value Run(const Napi::CallbackInfo &info)
67
- {
68
- if (!RG_UNLIKELY(Prepare(info)))
69
- return env.Null();
70
-
71
- if (debug) {
72
- DumpDebug();
73
- }
74
- Execute();
75
-
76
- return Complete();
77
- }
68
+ Napi::Value Run(const Napi::CallbackInfo &info);
78
69
 
79
70
  void DumpDebug() const;
80
71
 
package/src/ffi.cc CHANGED
@@ -261,6 +261,27 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
261
261
  return env.Null();
262
262
  }
263
263
 
264
+ TypeInfo::ArrayHint hint;
265
+ if (info.Length() >= 3 && !IsNullOrUndefined(info[2])) {
266
+ if (!info[2].IsString()) {
267
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for hint, expected string", GetValueType(instance, info[2]));
268
+ return env.Null();
269
+ }
270
+
271
+ std::string to = info[2].As<Napi::String>();
272
+
273
+ if (to == "typed") {
274
+ hint = TypeInfo::ArrayHint::TypedArray;
275
+ } else if (to == "array") {
276
+ hint = TypeInfo::ArrayHint::Array;
277
+ } else {
278
+ ThrowError<Napi::Error>(env, "Array conversion hint must be 'typed' or 'array'");
279
+ return env.Null();
280
+ }
281
+ } else {
282
+ hint = TypeInfo::ArrayHint::TypedArray;
283
+ }
284
+
264
285
  const TypeInfo *ref = ResolveType(instance, info[0]);
265
286
  int64_t len = (uint16_t)info[1].As<Napi::Number>().Int64Value();
266
287
 
@@ -284,6 +305,7 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
284
305
  type->align = ref->align;
285
306
  type->size = (int16_t)(len * ref->size);
286
307
  type->ref = ref;
308
+ type->hint = hint;
287
309
 
288
310
  // If the insert succeeds, we cannot fail anymore
289
311
  if (!instance->types_map.TrySet(type).second) {
@@ -395,13 +417,13 @@ static Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info)
395
417
  InstanceData *instance = env.GetInstanceData<InstanceData>();
396
418
  FunctionInfo *func = (FunctionInfo *)info.Data();
397
419
 
398
- if (info.Length() < (uint32_t)func->parameters.len) {
420
+ if (RG_UNLIKELY(info.Length() < (uint32_t)func->parameters.len)) {
399
421
  ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, info.Length());
400
422
  return env.Null();
401
423
  }
402
424
 
403
425
  InstanceMemory *mem = AllocateCallMemory(instance);
404
- CallData call(env, func, mem, instance->debug);
426
+ CallData call(env, instance, func, mem);
405
427
 
406
428
  return call.Run(info);
407
429
  }
@@ -421,11 +443,11 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
421
443
  func.parameters.Leak();
422
444
  };
423
445
 
424
- if (info.Length() < (uint32_t)func.parameters.len) {
446
+ if (RG_UNLIKELY(info.Length() < (uint32_t)func.parameters.len)) {
425
447
  ThrowError<Napi::TypeError>(env, "Expected %1 arguments or more, got %2", func.parameters.len, info.Length());
426
448
  return env.Null();
427
449
  }
428
- if ((info.Length() - func.parameters.len) % 2) {
450
+ if (RG_UNLIKELY((info.Length() - func.parameters.len) % 2)) {
429
451
  ThrowError<Napi::Error>(env, "Missing value argument for variadic call");
430
452
  return env.Null();
431
453
  }
@@ -434,19 +456,19 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
434
456
  ParameterInfo param = {};
435
457
 
436
458
  param.type = ResolveType(instance, info[i], &param.directions);
437
- if (!param.type)
459
+ if (RG_UNLIKELY(!param.type))
438
460
  return env.Null();
439
- if (param.type->primitive == PrimitiveKind::Void ||
440
- param.type->primitive == PrimitiveKind::Array) {
461
+ if (RG_UNLIKELY(param.type->primitive == PrimitiveKind::Void ||
462
+ param.type->primitive == PrimitiveKind::Array)) {
441
463
  ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter", PrimitiveKindNames[(int)param.type->primitive]);
442
464
  return env.Null();
443
465
  }
444
466
 
445
- if (func.parameters.len >= MaxParameters) {
467
+ if (RG_UNLIKELY(func.parameters.len >= MaxParameters)) {
446
468
  ThrowError<Napi::TypeError>(env, "Functions cannot have more than %1 parameters", MaxParameters);
447
469
  return env.Null();
448
470
  }
449
- if ((param.directions & 2) && ++func.out_parameters >= MaxOutParameters) {
471
+ if (RG_UNLIKELY((param.directions & 2) && ++func.out_parameters >= MaxOutParameters)) {
450
472
  ThrowError<Napi::TypeError>(env, "Functions cannot have more than out %1 parameters", MaxOutParameters);
451
473
  return env.Null();
452
474
  }
@@ -457,11 +479,11 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
457
479
  func.parameters.Append(param);
458
480
  }
459
481
 
460
- if (!AnalyseFunction(instance, &func))
482
+ if (RG_UNLIKELY(!AnalyseFunction(instance, &func)))
461
483
  return env.Null();
462
484
 
463
485
  InstanceMemory *mem = AllocateCallMemory(instance);
464
- CallData call(env, &func, mem, instance->debug);
486
+ CallData call(env, instance, &func, mem);
465
487
 
466
488
  return call.Run(info);
467
489
  }
@@ -474,10 +496,10 @@ class AsyncCall: public Napi::AsyncWorker {
474
496
  bool prepared = false;
475
497
 
476
498
  public:
477
- AsyncCall(Napi::Env env, InstanceMemory *mem, FunctionInfo *func, bool debug,
478
- Napi::Function &callback)
499
+ AsyncCall(Napi::Env env, InstanceData *instance, const FunctionInfo *func,
500
+ InstanceMemory *mem, Napi::Function &callback)
479
501
  : Napi::AsyncWorker(callback), env(env), func(func->Ref()),
480
- call(env, func, mem, debug) {}
502
+ call(env, instance, func, mem) {}
481
503
  ~AsyncCall() { func->Unref(); }
482
504
 
483
505
  bool Prepare(const Napi::CallbackInfo &info) {
@@ -537,7 +559,7 @@ static Napi::Value TranslateAsyncCall(const Napi::CallbackInfo &info)
537
559
  }
538
560
 
539
561
  InstanceMemory *mem = AllocateCallMemory(instance);
540
- AsyncCall *async = new AsyncCall(env, mem, func, instance->debug, callback);
562
+ AsyncCall *async = new AsyncCall(env, instance, func, mem, callback);
541
563
 
542
564
  if (async->Prepare(info) && instance->debug) {
543
565
  async->DumpDebug();
package/src/ffi.hh CHANGED
@@ -67,6 +67,11 @@ struct TypeInfo;
67
67
  struct RecordMember;
68
68
 
69
69
  struct TypeInfo {
70
+ enum class ArrayHint {
71
+ Array,
72
+ TypedArray
73
+ };
74
+
70
75
  const char *name;
71
76
  napi_type_tag tag;
72
77
 
@@ -78,6 +83,7 @@ struct TypeInfo {
78
83
 
79
84
  HeapArray<RecordMember> members; // Record only
80
85
  const TypeInfo *ref; // Pointer or array
86
+ ArrayHint hint; // Array only
81
87
 
82
88
  RG_HASHTABLE_HANDLER(TypeInfo, name);
83
89
  };
@@ -168,7 +174,6 @@ struct InstanceMemory {
168
174
 
169
175
  Span<uint8_t> stack;
170
176
  Span<uint8_t> heap;
171
- IndirectBlockAllocator big_alloc { &mem_alloc };
172
177
 
173
178
  int depth;
174
179
  bool temporary;