koffi 1.1.0-beta.6 → 1.1.0

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/README.md CHANGED
@@ -20,10 +20,10 @@
20
20
 
21
21
  Koffi is a fast and easy-to-use FFI module for Node.js, with support for primitive and aggregate data types (structs), both by reference (pointer) and by value.
22
22
 
23
- After the release of version 1.0, the following features are planned:
23
+ The following features are planned in the near future:
24
24
 
25
- * 1.1: Asynchronous calls and fixed-size array types
26
25
  * 1.2: C to JS callbacks
26
+ * 1.3: Type parser
27
27
 
28
28
  The following platforms __are officially supported and tested__ at the moment:
29
29
 
@@ -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.6",
3
+ "version": "1.1.0",
4
4
  "description": "Fast and simple FFI (foreign function interface) for Node.js",
5
5
  "keywords": [
6
6
  "foreign",
package/src/call.cc CHANGED
@@ -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,255 +483,113 @@ 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;
804
- } break;
805
- case PrimitiveKind::Float64: {
806
- Napi::Float64Array array = Napi::Float64Array::New(env, len);
807
-
808
- for (uint32_t i = 0; i < len; i++) {
809
- int16_t align = std::max(realign, type->ref->align);
810
- src = AlignUp(src, align);
811
-
812
- double d = *(double *)src;
813
- array[i] = d;
814
-
815
- src += type->ref->size;
816
- }
817
-
818
- return array;
584
+ });
819
585
  } break;
586
+ case PrimitiveKind::Float32: { POP_NUMBER_ARRAY(Float32Array, float); } break;
587
+ case PrimitiveKind::Float64: { POP_NUMBER_ARRAY(Float64Array, double); } break;
820
588
  }
821
589
 
590
+ #undef POP_NUMBER_ARRAY
591
+ #undef POP_ARRAY
592
+
822
593
  RG_UNREACHABLE();
823
594
  }
824
595
 
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,7 +417,7 @@ 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
  }
@@ -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,7 +479,7 @@ 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);
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
  };