koffi 3.0.2 → 3.1.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 (48) hide show
  1. package/CHANGELOG.md +32 -7
  2. package/cnoke.cjs +2 -2
  3. package/doc/benchmarks.md +2 -2
  4. package/doc/callbacks.md +8 -27
  5. package/doc/{input.md → composites.md} +161 -147
  6. package/doc/contribute.md +2 -1
  7. package/doc/index.md +0 -14
  8. package/doc/{functions.md → load.md} +54 -113
  9. package/doc/migration.md +4 -7
  10. package/doc/misc.md +0 -103
  11. package/doc/output.md +5 -11
  12. package/doc/pointers.md +76 -17
  13. package/doc/primitives.md +151 -0
  14. package/doc/start.md +3 -13
  15. package/doc/types.md +88 -0
  16. package/doc/unions.md +0 -186
  17. package/doc/values.md +134 -0
  18. package/index.d.ts +23 -4
  19. package/lib/native/base/base.cc +142 -90
  20. package/lib/native/base/base.hh +69 -165
  21. package/package.json +16 -15
  22. package/src/koffi/CMakeLists.txt +19 -17
  23. package/src/koffi/index.cjs +8 -3
  24. package/src/koffi/index.js +1 -1
  25. package/src/koffi/indirect.cjs +8 -3
  26. package/src/koffi/indirect.js +1 -1
  27. package/src/koffi/src/abi/arm64.cc +47 -62
  28. package/src/koffi/src/abi/riscv64.cc +38 -57
  29. package/src/koffi/src/abi/x64sysv.cc +38 -57
  30. package/src/koffi/src/abi/x64win.cc +47 -65
  31. package/src/koffi/src/abi/x86.cc +46 -59
  32. package/src/koffi/src/call.cc +239 -133
  33. package/src/koffi/src/call.hh +5 -10
  34. package/src/koffi/src/ffi.cc +211 -90
  35. package/src/koffi/src/ffi.hh +36 -16
  36. package/src/koffi/src/parser.cc +3 -3
  37. package/src/koffi/src/parser.hh +2 -2
  38. package/src/koffi/src/static.cjs +8 -0
  39. package/src/koffi/src/static.js +11 -0
  40. package/src/koffi/src/type.cc +43 -33
  41. package/src/koffi/src/type.hh +1 -1
  42. package/src/koffi/src/util.cc +73 -173
  43. package/src/koffi/src/util.hh +84 -43
  44. package/src/koffi/src/uv.cc +1 -1
  45. package/vendor/node-addon-api/README.md +1 -1
  46. package/vendor/node-addon-api/napi-inl.h +213 -35
  47. package/vendor/node-addon-api/napi.h +118 -7
  48. package/doc/variables.md +0 -102
@@ -186,15 +186,12 @@ void *MallocAllocator::Resize(void *ptr, Size old_size, Size new_size)
186
186
  {
187
187
  if (!new_size) {
188
188
  Release(ptr, old_size);
189
- ptr = nullptr;
190
- } else {
191
- void *new_ptr = realloc(ptr, (size_t)new_size);
192
- K_CRITICAL(new_ptr || !new_size, "Failed to resize %1 memory block to %2",
193
- FmtMemSize(old_size), FmtMemSize(new_size));
194
-
195
- ptr = new_ptr;
189
+ return nullptr;
196
190
  }
197
191
 
192
+ ptr = realloc(ptr, (size_t)new_size);
193
+ K_CRITICAL(ptr || !new_size, "Failed to resize %1 memory block to %2", FmtMemSize(old_size), FmtMemSize(new_size));
194
+
198
195
  return ptr;
199
196
  }
200
197
 
@@ -216,6 +213,8 @@ Allocator *GetNullAllocator()
216
213
  LinkedAllocator& LinkedAllocator::operator=(LinkedAllocator &&other)
217
214
  {
218
215
  ReleaseAll();
216
+
217
+ allocator = other.allocator;
219
218
  list = other.list;
220
219
  other.list = nullptr;
221
220
 
@@ -363,10 +362,13 @@ BlockAllocator& BlockAllocator::operator=(BlockAllocator &&other)
363
362
  allocator.operator=(std::move(other.allocator));
364
363
 
365
364
  block_size = other.block_size;
366
- current_bucket = other.current_bucket;
365
+
366
+ bucket_ptr = other.bucket_ptr;
367
+ bucket_end = other.bucket_end;
367
368
  last_alloc = other.last_alloc;
368
369
 
369
- other.current_bucket = nullptr;
370
+ other.bucket_ptr = nullptr;
371
+ other.bucket_end = nullptr;
370
372
  other.last_alloc = nullptr;
371
373
 
372
374
  return *this;
@@ -374,19 +376,23 @@ BlockAllocator& BlockAllocator::operator=(BlockAllocator &&other)
374
376
 
375
377
  void BlockAllocator::Reset()
376
378
  {
377
- last_alloc = nullptr;
379
+ if (bucket_ptr) {
380
+ bucket_ptr = bucket_end - block_size;
381
+
382
+ // Not true but Resize code assumes that last_alloc is not null when a bucket exists.
383
+ // I could also let it as-is, but for some reason I find that less clean.
384
+ last_alloc = bucket_ptr;
378
385
 
379
- if (current_bucket) {
380
- current_bucket->used = 0;
381
- allocator.ReleaseAllExcept(current_bucket);
386
+ allocator.ReleaseAllExcept(bucket_ptr);
382
387
  } else {
383
- allocator.ReleaseAll();
388
+ ReleaseAll();
384
389
  }
385
390
  }
386
391
 
387
392
  void BlockAllocator::ReleaseAll()
388
393
  {
389
- current_bucket = nullptr;
394
+ bucket_ptr = nullptr;
395
+ bucket_end = nullptr;
390
396
  last_alloc = nullptr;
391
397
 
392
398
  allocator.ReleaseAll();
@@ -396,88 +402,78 @@ void *BlockAllocator::Allocate(Size size)
396
402
  {
397
403
  K_ASSERT(size >= 0);
398
404
 
399
- // Keep alignement requirements
400
- Size aligned_size = AlignLen(size, 8);
405
+ uint8_t *ptr = bucket_ptr;
406
+
407
+ // Fast path
408
+ if (uint8_t *new_ptr = AlignUp(ptr + size, 8); new_ptr <= bucket_end) [[likely]] {
409
+ bucket_ptr = new_ptr;
401
410
 
402
- if (AllocateSeparately(aligned_size)) {
403
- uint8_t *ptr = (uint8_t *)allocator.Allocate(size);
411
+ last_alloc = ptr;
404
412
  return ptr;
405
- } else {
406
- if (!current_bucket || (current_bucket->used + aligned_size) > block_size) {
407
- current_bucket = (Bucket *)allocator.Allocate(K_SIZE(Bucket) + block_size);
408
- current_bucket->used = 0;
409
- }
413
+ }
414
+
415
+ if (size <= block_size) {
416
+ bucket_ptr = (uint8_t *)allocator.Allocate(block_size);
417
+ bucket_end = bucket_ptr + block_size;
410
418
 
411
- uint8_t *ptr = current_bucket->data + current_bucket->used;
412
- current_bucket->used += aligned_size;
419
+ ptr = bucket_ptr;
420
+ bucket_ptr = AlignUp(ptr + size, 8);
413
421
 
414
422
  last_alloc = ptr;
415
423
  return ptr;
416
424
  }
425
+
426
+ // Fallback for big allocations
427
+ return allocator.Allocate(size);
417
428
  }
418
429
 
419
430
  void *BlockAllocator::Resize(void *ptr, Size old_size, Size new_size)
420
431
  {
421
432
  K_ASSERT(old_size >= 0);
422
433
  K_ASSERT(new_size >= 0);
434
+ K_ASSERT(ptr || !old_size);
423
435
 
424
436
  if (!new_size) {
425
437
  Release(ptr, old_size);
426
- ptr = nullptr;
427
- } else {
428
- if (!ptr) {
429
- old_size = 0;
430
- }
438
+ return nullptr;
439
+ }
431
440
 
432
- Size aligned_old_size = AlignLen(old_size, 8);
433
- Size aligned_new_size = AlignLen(new_size, 8);
434
- Size aligned_delta = aligned_new_size - aligned_old_size;
441
+ // Fast path
442
+ if (uint8_t *new_ptr = AlignUp((uint8_t *)ptr + new_size, 8); ptr == last_alloc && new_ptr <= bucket_end) {
443
+ K_ASSERT(ptr);
435
444
 
436
- // Try fast path
437
- if (ptr && ptr == last_alloc &&
438
- (current_bucket->used + aligned_delta) <= block_size &&
439
- !AllocateSeparately(aligned_new_size)) {
440
- current_bucket->used += aligned_delta;
441
- } else if (AllocateSeparately(aligned_old_size)) {
442
- ptr = allocator.Resize(ptr, old_size, new_size);
443
- } else {
444
- void *new_ptr = Allocate(new_size);
445
+ bucket_ptr = new_ptr;
446
+ return ptr;
447
+ }
445
448
 
446
- Size copy = std::min(old_size, new_size);
447
- MemCpy(new_ptr, ptr, copy);
449
+ if (old_size <= block_size) {
450
+ uint8_t *copy_ptr = (uint8_t *)Allocate(new_size);
451
+ Size copy_len = std::min(old_size, new_size);
448
452
 
449
- ptr = new_ptr;
450
- }
453
+ MemCpy(copy_ptr, ptr, copy_len);
454
+
455
+ return copy_ptr;
451
456
  }
452
457
 
453
- return ptr;
458
+ // Old allocation fell back to separate allocation, we can resize at will now!
459
+ return allocator.Resize(ptr, old_size, new_size);
454
460
  }
455
461
 
456
462
  void BlockAllocator::Release(const void *ptr, Size size)
457
463
  {
458
464
  K_ASSERT(size >= 0);
459
465
 
460
- if (ptr) {
461
- Size aligned_size = AlignLen(size, 8);
462
-
463
- if (ptr == last_alloc) {
464
- current_bucket->used -= aligned_size;
465
-
466
- if (!current_bucket->used) {
467
- allocator.Release(current_bucket, K_SIZE(Bucket) + block_size);
468
- current_bucket = nullptr;
469
- }
470
-
471
- last_alloc = nullptr;
472
- } else if (AllocateSeparately(aligned_size)) {
473
- allocator.Release(ptr, size);
474
- }
466
+ if (ptr == last_alloc) {
467
+ bucket_ptr = last_alloc;
468
+ } else if (size > block_size) {
469
+ allocator.Release(ptr, size);
475
470
  }
476
471
  }
477
472
 
478
473
  void BlockAllocator::GiveTo(LinkedAllocator *alloc)
479
474
  {
480
- current_bucket = nullptr;
475
+ bucket_ptr = nullptr;
476
+ bucket_end = nullptr;
481
477
  last_alloc = nullptr;
482
478
 
483
479
  allocator.GiveTo(alloc);
@@ -1511,7 +1507,7 @@ static inline void ProcessArg(const FmtArg &arg, AppendFunc append)
1511
1507
  append(FormatUnsignedToDecimal((uint64_t)arg.u.date.st.day, buf));
1512
1508
  } break;
1513
1509
 
1514
- case FmtType::TimeISO: {
1510
+ case FmtType::TimeBasic: {
1515
1511
  const TimeSpec &spec = arg.u.time.spec;
1516
1512
 
1517
1513
  LocalArray<char, 128> buf;
@@ -1548,6 +1544,43 @@ static inline void ProcessArg(const FmtArg &arg, AppendFunc append)
1548
1544
 
1549
1545
  append(buf);
1550
1546
  } break;
1547
+ case FmtType::TimeIso: {
1548
+ const TimeSpec &spec = arg.u.time.spec;
1549
+
1550
+ LocalArray<char, 128> buf;
1551
+
1552
+ if (spec.offset && arg.u.time.ms) {
1553
+ int offset_h = spec.offset / 60;
1554
+ int offset_m = spec.offset % 60;
1555
+
1556
+ buf.len = Fmt(buf.data, "%1-%2-%3T%4:%5:%6.%7%8%9%10",
1557
+ FmtInt(spec.year, 2), FmtInt(spec.month, 2),
1558
+ FmtInt(spec.day, 2), FmtInt(spec.hour, 2),
1559
+ FmtInt(spec.min, 2), FmtInt(spec.sec, 2), FmtInt(spec.msec, 3),
1560
+ offset_h >= 0 ? "+" : "", FmtInt(offset_h, 2), FmtInt(offset_m, 2)).len;
1561
+ } else if (spec.offset) {
1562
+ int offset_h = spec.offset / 60;
1563
+ int offset_m = spec.offset % 60;
1564
+
1565
+ buf.len = Fmt(buf.data, "%1-%2-%3T%4:%5:%6%7%8%9",
1566
+ FmtInt(spec.year, 2), FmtInt(spec.month, 2),
1567
+ FmtInt(spec.day, 2), FmtInt(spec.hour, 2),
1568
+ FmtInt(spec.min, 2), FmtInt(spec.sec, 2),
1569
+ offset_h >= 0 ? "+" : "", FmtInt(offset_h, 2), FmtInt(offset_m, 2)).len;
1570
+ } else if (arg.u.time.ms) {
1571
+ buf.len = Fmt(buf.data, "%1-%2-%3T%4:%5:%6.%7Z",
1572
+ FmtInt(spec.year, 2), FmtInt(spec.month, 2),
1573
+ FmtInt(spec.day, 2), FmtInt(spec.hour, 2),
1574
+ FmtInt(spec.min, 2), FmtInt(spec.sec, 2), FmtInt(spec.msec, 3)).len;
1575
+ } else {
1576
+ buf.len = Fmt(buf.data, "%1-%2-%3T%4:%5:%6Z",
1577
+ FmtInt(spec.year, 2), FmtInt(spec.month, 2),
1578
+ FmtInt(spec.day, 2), FmtInt(spec.hour, 2),
1579
+ FmtInt(spec.min, 2), FmtInt(spec.sec, 2)).len;
1580
+ }
1581
+
1582
+ append(buf);
1583
+ } break;
1551
1584
  case FmtType::TimeNice: {
1552
1585
  const TimeSpec &spec = arg.u.time.spec;
1553
1586
 
@@ -1947,7 +1980,7 @@ void FmtUrlSafe::Format(FunctionRef<void(Span<const char>)> append) const
1947
1980
  }
1948
1981
  }
1949
1982
 
1950
- void FmtHtmlSafe::Format(FunctionRef<void(Span<const char>)> append) const
1983
+ void FmtXmlSafe::Format(FunctionRef<void(Span<const char>)> append) const
1951
1984
  {
1952
1985
  for (char c: str) {
1953
1986
  switch (c) {
@@ -5171,7 +5204,7 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
5171
5204
 
5172
5205
  // Read and write standard process streams
5173
5206
  {
5174
- bool running = true;
5207
+ bool running = in_func.IsValid() || out_func.IsValid();
5175
5208
 
5176
5209
  PendingIO proc_in;
5177
5210
  Span<const uint8_t> write_buf = {};
@@ -5201,6 +5234,7 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
5201
5234
  }
5202
5235
  } else {
5203
5236
  CloseHandleSafe(&in_pipe[1]);
5237
+ running &= out_func.IsValid();
5204
5238
  }
5205
5239
  }
5206
5240
 
@@ -5632,7 +5666,7 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
5632
5666
  return true;
5633
5667
  }
5634
5668
 
5635
- Size ReadCommandOutput(const char *cmd_line, Span<char> out_output)
5669
+ Size ReadCommandOutput(const char *cmd_line, Span<uint8_t> out_output)
5636
5670
  {
5637
5671
  static ExecuteInfo::KeyValue variables[] = {
5638
5672
  { "LANG", "C" },
@@ -5651,34 +5685,48 @@ Size ReadCommandOutput(const char *cmd_line, Span<char> out_output)
5651
5685
  };
5652
5686
 
5653
5687
  int exit_code;
5654
- if (!ExecuteCommandLine(cmd_line, info, MakeSpan((const uint8_t *)nullptr, 0), write, &exit_code))
5688
+ if (!ExecuteCommandLine(cmd_line, info, MakeSpan((const uint8_t *)nullptr, 0), write, &exit_code)) {
5689
+ if (out_output.len) {
5690
+ Span<const char> output = out_output.As<const char>();
5691
+ LogError("Command '%1' failed: %2", cmd_line, output);
5692
+ }
5655
5693
  return -1;
5694
+ }
5656
5695
  if (exit_code) {
5657
- LogDebug("Command '%1 failed (exit code: %2)", cmd_line, exit_code);
5696
+ LogDebug("Command '%1' failed (exit code: %2)", cmd_line, exit_code);
5658
5697
  return -1;
5659
5698
  }
5660
5699
 
5661
5700
  return total_len;
5662
5701
  }
5663
5702
 
5664
- bool ReadCommandOutput(const char *cmd_line, HeapArray<char> *out_output)
5703
+ bool ReadCommandOutput(const char *cmd_line, HeapArray<uint8_t> *out_output)
5665
5704
  {
5666
5705
  static ExecuteInfo::KeyValue variables[] = {
5667
5706
  { "LANG", "C" },
5668
5707
  { "LC_ALL", "C" }
5669
5708
  };
5670
5709
 
5710
+ Size start_len = out_output->len;
5711
+ K_DEFER_N(err_guard) { out_output->RemoveFrom(start_len); };
5712
+
5671
5713
  ExecuteInfo info = {};
5672
5714
  info.env_variables = variables;
5673
5715
 
5674
5716
  int exit_code;
5675
- if (!ExecuteCommandLine(cmd_line, info, {}, Mebibytes(1), out_output, &exit_code))
5717
+ if (!ExecuteCommandLine(cmd_line, info, {}, Mebibytes(1), out_output, &exit_code)) {
5718
+ if (out_output->len > start_len) {
5719
+ Span<const char> output = out_output->Take(start_len, out_output->len - start_len).As<const char>();
5720
+ LogError("Command '%1' failed: %2", cmd_line, output);
5721
+ }
5676
5722
  return false;
5723
+ }
5677
5724
  if (exit_code) {
5678
- LogDebug("Command '%1 failed (exit code: %2)", cmd_line, exit_code);
5725
+ LogDebug("Command '%1' failed (exit code: %2)", cmd_line, exit_code);
5679
5726
  return false;
5680
5727
  }
5681
5728
 
5729
+ err_guard.Disable();
5682
5730
  return true;
5683
5731
  }
5684
5732
 
@@ -7405,7 +7453,7 @@ public:
7405
7453
  void AddTask(Async *async, int worker_idx, const std::function<bool()> &func);
7406
7454
 
7407
7455
  void RunWorker(int worker_idx);
7408
- void SyncOn(Async *async, bool soon);
7456
+ void SyncOn(Async *async, Async *only);
7409
7457
  bool WaitOn(Async *async, int timeout);
7410
7458
 
7411
7459
  void RunTasks(int worker_idx, Async *only);
@@ -7419,7 +7467,7 @@ static thread_local AsyncPool *async_running_pool = nullptr;
7419
7467
  static thread_local int async_running_worker_idx;
7420
7468
  static thread_local bool async_running_task = false;
7421
7469
 
7422
- Async::Async(int threads)
7470
+ Async::Async(int threads, unsigned int flags)
7423
7471
  {
7424
7472
  K_ASSERT(threads);
7425
7473
 
@@ -7441,14 +7489,22 @@ Async::Async(int threads)
7441
7489
  }
7442
7490
 
7443
7491
  pool->RegisterAsync();
7492
+
7493
+ if (flags & (int)AsyncFlag::Selfish) {
7494
+ only = this;
7495
+ }
7444
7496
  }
7445
7497
 
7446
- Async::Async(Async *parent)
7498
+ Async::Async(Async *parent, unsigned int flags)
7447
7499
  {
7448
7500
  K_ASSERT(parent);
7449
7501
 
7450
7502
  pool = parent->pool;
7451
7503
  pool->RegisterAsync();
7504
+
7505
+ if (flags & (int)AsyncFlag::Selfish) {
7506
+ only = this;
7507
+ }
7452
7508
  }
7453
7509
 
7454
7510
  Async::~Async()
@@ -7471,13 +7527,7 @@ void Async::Run(int worker, const std::function<bool()> &func)
7471
7527
 
7472
7528
  bool Async::Sync()
7473
7529
  {
7474
- pool->SyncOn(this, false);
7475
- return success;
7476
- }
7477
-
7478
- bool Async::SyncSoon()
7479
- {
7480
- pool->SyncOn(this, true);
7530
+ pool->SyncOn(this, only);
7481
7531
  return success;
7482
7532
  }
7483
7533
 
@@ -7613,7 +7663,7 @@ void AsyncPool::AddTask(Async *async, int worker_idx, const std::function<bool()
7613
7663
  int worker_idx = async_running_worker_idx;
7614
7664
 
7615
7665
  do {
7616
- RunTasks(worker_idx, nullptr);
7666
+ RunTasks(worker_idx, async->only);
7617
7667
  } while (pending_tasks >= K_ASYNC_MAX_PENDING_TASKS);
7618
7668
  } else if (!prev_pending) {
7619
7669
  std::lock_guard<std::mutex> lock_pool(pool_mutex);
@@ -7647,7 +7697,7 @@ void AsyncPool::RunWorker(int worker_idx)
7647
7697
  }
7648
7698
  }
7649
7699
 
7650
- void AsyncPool::SyncOn(Async *async, bool soon)
7700
+ void AsyncPool::SyncOn(Async *async, Async *only)
7651
7701
  {
7652
7702
  K_DEFER_C(pool = async_running_pool,
7653
7703
  worker_idx = async_running_worker_idx) {
@@ -7659,7 +7709,7 @@ void AsyncPool::SyncOn(Async *async, bool soon)
7659
7709
  async_running_worker_idx = 0;
7660
7710
 
7661
7711
  while (async->remaining_tasks) {
7662
- RunTasks(0, soon ? async : nullptr);
7712
+ RunTasks(0, only);
7663
7713
 
7664
7714
  std::unique_lock<std::mutex> lock_sync(pool_mutex);
7665
7715
  sync_cv.wait(lock_sync, [&]() { return pending_tasks || !async->remaining_tasks; });
@@ -7739,12 +7789,12 @@ void AsyncPool::RunTask(Task *task)
7739
7789
  #else
7740
7790
 
7741
7791
 
7742
- Async::Async(int threads)
7792
+ Async::Async(int threads, unsigned int)
7743
7793
  {
7744
7794
  K_ASSERT(threads);
7745
7795
  }
7746
7796
 
7747
- Async::Async(Async *parent)
7797
+ Async::Async(Async *parent, unsigned int)
7748
7798
  {
7749
7799
  K_ASSERT(parent);
7750
7800
  }
@@ -10728,7 +10778,7 @@ const char *GetMimeType(Span<const char> extension, const char *default_type)
10728
10778
  const char *mimetype = mimetypes.FindValue(lower, nullptr);
10729
10779
 
10730
10780
  if (!mimetype) {
10731
- LogError("Unknown MIME type for extension '%1'", extension);
10781
+ LogDebug("Unknown MIME type for extension '%1'", extension);
10732
10782
  mimetype = default_type;
10733
10783
  }
10734
10784
 
@@ -10768,8 +10818,10 @@ bool CanCompressFile(const char *filename)
10768
10818
  if (TestStrI(extension, ".db") || TestStrI(extension, ".sqlite3"))
10769
10819
  return false;
10770
10820
 
10771
- const char *mimetype = GetMimeType(extension);
10821
+ const char *mimetype = GetMimeType(extension, nullptr);
10772
10822
 
10823
+ if (!mimetype)
10824
+ return false;
10773
10825
  if (StartsWith(mimetype, "video/"))
10774
10826
  return false;
10775
10827
  if (StartsWith(mimetype, "audio/"))