koffi 2.6.3 → 2.6.4

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/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  ### Koffi 2.6
6
6
 
7
+ #### Koffi 2.6.4 (2023-10-26)
8
+
9
+ - Fix build issue with recent Visual Studio versions
10
+
7
11
  #### Koffi 2.6.3 (2023-10-17)
8
12
 
9
13
  - Add indirect loading script to help some bundlers
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/doc/contribute.md CHANGED
@@ -139,7 +139,7 @@ Once this is done, you can publish a new release with the following commands:
139
139
 
140
140
  ```sh
141
141
  node tools/qemu.js test # If not done before
142
- node tools/qemu.js dist
142
+ node tools/qemu.js build
143
143
 
144
144
  cd build/dist
145
145
  npm publish
package/doc/packaging.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Bundlers and Koffi
2
2
 
3
- ## Bundling and native modules
3
+ ## Bundling
4
+
5
+ ### Native modules
4
6
 
5
7
  *Simplified in Koffi 2.5.9*
6
8
 
@@ -36,7 +38,7 @@ resources/
36
38
  MyApp.exe
37
39
  ```
38
40
 
39
- ## Indirect loader
41
+ ### Indirect loader
40
42
 
41
43
  *New in Koffi 2.6.2*
42
44
 
package/index.js CHANGED
@@ -378,8 +378,8 @@ var require_package = __commonJS({
378
378
  "build/dist/src/koffi/package.json"(exports2, module2) {
379
379
  module2.exports = {
380
380
  name: "koffi",
381
- version: "2.6.3",
382
- stable: "2.6.3",
381
+ version: "2.6.4",
382
+ stable: "2.6.4",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
package/indirect.js CHANGED
@@ -378,8 +378,8 @@ var require_package = __commonJS({
378
378
  "build/dist/src/koffi/package.json"(exports2, module2) {
379
379
  module2.exports = {
380
380
  name: "koffi",
381
- version: "2.6.3",
382
- stable: "2.6.3",
381
+ version: "2.6.4",
382
+ stable: "2.6.4",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.6.3",
4
- "stable": "2.6.3",
3
+ "version": "2.6.4",
4
+ "stable": "2.6.4",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -1426,7 +1426,7 @@ static inline void ProcessArg(const FmtArg &arg, AppendFunc append)
1426
1426
  RG_ASSERT(arg.u.random.len <= RG_SIZE(out_buf.data));
1427
1427
 
1428
1428
  for (Size j = 0; j < arg.u.random.len; j++) {
1429
- int rnd = GetRandomIntSafe(0, (int)chars.len);
1429
+ int rnd = GetRandomIntFast(0, (int)chars.len);
1430
1430
  out_buf.Append(chars[rnd]);
1431
1431
  }
1432
1432
 
@@ -2146,37 +2146,6 @@ fail:
2146
2146
  return str_buf;
2147
2147
  }
2148
2148
 
2149
- void SetEnvironmentVar(const char *name, const char *value)
2150
- {
2151
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2152
- RG_ASSERT(value);
2153
-
2154
- if (win32_utf8) {
2155
- RG_CRITICAL(SetEnvironmentVariableA(name, value), "Failed to set environment variable '%1' to '%2': %3", name, value, GetWin32ErrorString());
2156
- } else {
2157
- wchar_t name_w[256];
2158
- wchar_t value_w[4096];
2159
-
2160
- RG_CRITICAL(ConvertUtf8ToWin32Wide(name, name_w) >= 0, "Failed to set environment variable '%1' to '%2'", name, value);
2161
- RG_CRITICAL(ConvertUtf8ToWin32Wide(value, value_w) >= 0, "Failed to set environment variable '%1' to '%2'", name, value);
2162
- RG_CRITICAL(SetEnvironmentVariableW(name_w, value_w), "Failed to set environment variable '%1' to '%2': %3", name, value, GetWin32ErrorString());
2163
- }
2164
- }
2165
-
2166
- void DeleteEnvironmentVar(const char *name)
2167
- {
2168
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2169
-
2170
- if (win32_utf8) {
2171
- RG_CRITICAL(SetEnvironmentVariableA(name, nullptr), "Failed to clear environment variable '%1': %2", name, GetWin32ErrorString());
2172
- } else {
2173
- wchar_t name_w[256];
2174
-
2175
- RG_CRITICAL(ConvertUtf8ToWin32Wide(name, name_w) >= 0, "Failed to clear environment variable '%1'", name);
2176
- RG_CRITICAL(SetEnvironmentVariableW(name_w, nullptr), "Failed to clear environment variable '%1': %2", name, GetWin32ErrorString());
2177
- }
2178
- }
2179
-
2180
2149
  static FileType FileAttributesToType(uint32_t attr)
2181
2150
  {
2182
2151
  if (attr & FILE_ATTRIBUTE_DIRECTORY) {
@@ -2360,20 +2329,6 @@ EnumResult EnumerateDirectory(const char *dirname, const char *filter, Size max_
2360
2329
 
2361
2330
  #else
2362
2331
 
2363
- void SetEnvironmentVar(const char *name, const char *value)
2364
- {
2365
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2366
- RG_ASSERT(value);
2367
-
2368
- RG_CRITICAL(!setenv(name, value, 1), "Failed to set environment variable '%1' to '%2': %3", name, value, strerror(errno));
2369
- }
2370
-
2371
- void DeleteEnvironmentVar(const char *name)
2372
- {
2373
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2374
- RG_CRITICAL(!unsetenv(name), "Failed to clear environment variable '%1': %2", name, strerror(errno));
2375
- }
2376
-
2377
2332
  static FileType FileModeToType(mode_t mode)
2378
2333
  {
2379
2334
  if (S_ISDIR(mode)) {
@@ -3919,7 +3874,7 @@ struct PendingIO {
3919
3874
  }
3920
3875
  };
3921
3876
 
3922
- bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
3877
+ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
3923
3878
  FunctionRef<Span<const uint8_t>()> in_func,
3924
3879
  FunctionRef<void(Span<uint8_t> buf)> out_func, int *out_code)
3925
3880
  {
@@ -3934,9 +3889,9 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
3934
3889
 
3935
3890
  // Convert work directory
3936
3891
  Span<wchar_t> work_dir_w;
3937
- if (work_dir) {
3938
- work_dir_w = AllocateSpan<wchar_t>(&temp_alloc, 2 * strlen(work_dir) + 1);
3939
- if (ConvertUtf8ToWin32Wide(work_dir, work_dir_w) < 0)
3892
+ if (info.work_dir) {
3893
+ work_dir_w = AllocateSpan<wchar_t>(&temp_alloc, 2 * strlen(info.work_dir) + 1);
3894
+ if (ConvertUtf8ToWin32Wide(info.work_dir, work_dir_w) < 0)
3940
3895
  return false;
3941
3896
  } else {
3942
3897
  work_dir_w = {};
@@ -3988,6 +3943,45 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
3988
3943
  if (out_func.IsValid() && !CreateOverlappedPipe(true, false, PipeMode::Byte, out_pipe))
3989
3944
  return false;
3990
3945
 
3946
+ // Prepare environment (if needed)
3947
+ HeapArray<wchar_t> new_env_w;
3948
+ if (info.reset_env || info.env_variables.len) {
3949
+ if (!info.reset_env) {
3950
+ Span<wchar_t> current_env = MakeSpan(GetEnvironmentStringsW(), 0);
3951
+
3952
+ do {
3953
+ Size len = (Size)wcslen(current_env.end());
3954
+ current_env.len += len + 1;
3955
+ } while (current_env.ptr[current_env.len]);
3956
+
3957
+ new_env_w.Append(current_env);
3958
+ }
3959
+
3960
+ for (const ExecuteInfo::KeyValue &kv: info.env_variables) {
3961
+ Span<const char> key = kv.key;
3962
+ Span<const char> value = kv.value;
3963
+
3964
+ Size len = 2 * (key.len + value.len + 1) + 1;
3965
+ new_env_w.Reserve(len);
3966
+
3967
+ len = ConvertUtf8ToWin32Wide(key, new_env_w.TakeAvailable());
3968
+ if (len < 0) [[unlikely]]
3969
+ return false;
3970
+ new_env_w.len += len;
3971
+
3972
+ new_env_w.Append(L'=');
3973
+
3974
+ len = ConvertUtf8ToWin32Wide(value, new_env_w.TakeAvailable());
3975
+ if (len < 0) [[unlikely]]
3976
+ return false;
3977
+ new_env_w.len += len;
3978
+
3979
+ new_env_w.Append(0);
3980
+ }
3981
+
3982
+ new_env_w.Append(0);
3983
+ }
3984
+
3991
3985
  // Start process
3992
3986
  HANDLE process_handle;
3993
3987
  {
@@ -4012,9 +4006,11 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4012
4006
  si.dwFlags |= STARTF_USESTDHANDLES;
4013
4007
  }
4014
4008
 
4009
+ int flags = CREATE_NEW_PROCESS_GROUP | CREATE_UNICODE_ENVIRONMENT;
4010
+
4015
4011
  PROCESS_INFORMATION pi = {};
4016
- if (!CreateProcessW(nullptr, cmd_line_w.ptr, nullptr, nullptr, TRUE, CREATE_NEW_PROCESS_GROUP,
4017
- nullptr, work_dir_w.ptr, &si, &pi)) {
4012
+ if (!CreateProcessW(nullptr, cmd_line_w.ptr, nullptr, nullptr, TRUE, flags,
4013
+ new_env_w.ptr, work_dir_w.ptr, &si, &pi)) {
4018
4014
  LogError("Failed to start process: %1", GetWin32ErrorString());
4019
4015
  return false;
4020
4016
  }
@@ -4223,10 +4219,12 @@ void CloseDescriptorSafe(int *fd_ptr)
4223
4219
  *fd_ptr = -1;
4224
4220
  }
4225
4221
 
4226
- bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4222
+ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
4227
4223
  FunctionRef<Span<const uint8_t>()> in_func,
4228
4224
  FunctionRef<void(Span<uint8_t> buf)> out_func, int *out_code)
4229
4225
  {
4226
+ BlockAllocator temp_alloc;
4227
+
4230
4228
  // Create read pipes
4231
4229
  int in_pfd[2] = {-1, -1};
4232
4230
  RG_DEFER {
@@ -4277,6 +4275,26 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4277
4275
  }
4278
4276
  }
4279
4277
 
4278
+ // Prepare new environment (if needed)
4279
+ HeapArray<char *> new_env;
4280
+ if (info.reset_env || info.env_variables.len) {
4281
+ if (!info.reset_env) {
4282
+ char **ptr = environ;
4283
+
4284
+ while (*ptr) {
4285
+ new_env.Append(*ptr);
4286
+ ptr++;
4287
+ }
4288
+ }
4289
+
4290
+ for (const ExecuteInfo::KeyValue &kv: info.env_variables) {
4291
+ const char *var = Fmt(&temp_alloc, "%1=%2", kv.key, kv.value).ptr;
4292
+ new_env.Append((char *)var);
4293
+ }
4294
+
4295
+ new_env.Append(nullptr);
4296
+ }
4297
+
4280
4298
  // Start process
4281
4299
  pid_t pid;
4282
4300
  {
@@ -4297,12 +4315,12 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4297
4315
  return false;
4298
4316
  }
4299
4317
 
4300
- if (work_dir) {
4301
- const char *argv[] = {"env", "-C", work_dir, "sh", "-c", cmd_line, nullptr };
4302
- errno = posix_spawn(&pid, "/bin/env", &file_actions, nullptr, const_cast<char **>(argv), environ);
4318
+ if (info.work_dir) {
4319
+ const char *argv[] = {"env", "-C", info.work_dir, "sh", "-c", cmd_line, nullptr };
4320
+ errno = posix_spawn(&pid, "/bin/env", &file_actions, nullptr, const_cast<char **>(argv), new_env.ptr ? new_env.ptr : environ);
4303
4321
  } else {
4304
4322
  const char *argv[] = {"sh", "-c", cmd_line, nullptr};
4305
- errno = posix_spawn(&pid, "/bin/sh", &file_actions, nullptr, const_cast<char **>(argv), environ);
4323
+ errno = posix_spawn(&pid, "/bin/sh", &file_actions, nullptr, const_cast<char **>(argv), new_env.ptr ? new_env.ptr : environ);
4306
4324
  }
4307
4325
  if (errno) {
4308
4326
  LogError("Failed to start process: %1", strerror(errno));
@@ -4444,7 +4462,7 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4444
4462
 
4445
4463
  #endif
4446
4464
 
4447
- bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4465
+ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
4448
4466
  Span<const uint8_t> in_buf, Size max_len,
4449
4467
  HeapArray<uint8_t> *out_buf, int *out_code)
4450
4468
  {
@@ -4467,8 +4485,8 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4467
4485
  // Don't f*ck up the log
4468
4486
  bool warned = false;
4469
4487
 
4470
- bool success = ExecuteCommandLine(cmd_line, work_dir, [&]() { return in_buf; },
4471
- [&](Span<uint8_t> buf) {
4488
+ bool success = ExecuteCommandLine(cmd_line, info, [&]() { return in_buf; },
4489
+ [&](Span<uint8_t> buf) {
4472
4490
  if (out_buf->len - start_len <= max_len - buf.len) {
4473
4491
  out_buf->Append(buf);
4474
4492
  } else if (!warned) {
@@ -4494,7 +4512,7 @@ Size ReadCommandOutput(const char *cmd_line, Span<char> out_output)
4494
4512
  };
4495
4513
 
4496
4514
  int exit_code;
4497
- if (!ExecuteCommandLine(cmd_line, nullptr, MakeSpan((const uint8_t *)nullptr, 0), write, &exit_code))
4515
+ if (!ExecuteCommandLine(cmd_line, {}, MakeSpan((const uint8_t *)nullptr, 0), write, &exit_code))
4498
4516
  return -1;
4499
4517
  if (exit_code) {
4500
4518
  LogDebug("Command '%1 failed (exit code: %2)", cmd_line, exit_code);
@@ -4507,7 +4525,7 @@ Size ReadCommandOutput(const char *cmd_line, Span<char> out_output)
4507
4525
  bool ReadCommandOutput(const char *cmd_line, HeapArray<char> *out_output)
4508
4526
  {
4509
4527
  int exit_code;
4510
- if (!ExecuteCommandLine(cmd_line, nullptr, {}, Mebibytes(1), out_output, &exit_code))
4528
+ if (!ExecuteCommandLine(cmd_line, {}, {}, Mebibytes(1), out_output, &exit_code))
4511
4529
  return false;
4512
4530
  if (exit_code) {
4513
4531
  LogDebug("Command '%1 failed (exit code: %2)", cmd_line, exit_code);
@@ -4759,7 +4777,7 @@ bool NotifySystemd()
4759
4777
  return false;
4760
4778
  }
4761
4779
 
4762
- DeleteEnvironmentVar("NOTIFY_SOCKET");
4780
+ unsetenv("NOTIFY_SOCKET");
4763
4781
  return true;
4764
4782
  }
4765
4783
  #endif
@@ -5063,79 +5081,6 @@ const char *CreateUniqueDirectory(Span<const char> directory, const char *prefix
5063
5081
  // Random
5064
5082
  // ------------------------------------------------------------------------
5065
5083
 
5066
- static inline uint32_t ROTL32(uint32_t v, int n)
5067
- {
5068
- return (v << n) | (v >> (32 - n));
5069
- }
5070
-
5071
- static inline uint64_t ROTL64(uint64_t v, int n) {
5072
- return (v << n) | (v >> (64 - n));
5073
- }
5074
-
5075
- FastRandom::FastRandom()
5076
- {
5077
- do {
5078
- FillRandomSafe(state, RG_SIZE(state));
5079
- } while (std::all_of(std::begin(state), std::end(state), [](uint64_t v) { return !v; }));
5080
- }
5081
-
5082
- FastRandom::FastRandom(uint64_t seed)
5083
- {
5084
- // splitmix64 generator to seed xoshiro256++, as recommended
5085
-
5086
- seed += 0x9e3779b97f4a7c15;
5087
-
5088
- for (int i = 0; i < 4; i++) {
5089
- seed = (seed ^ (seed >> 30)) * 0xbf58476d1ce4e5b9;
5090
- seed = (seed ^ (seed >> 27)) * 0x94d049bb133111eb;
5091
- state[i] = seed ^ (seed >> 31);
5092
- }
5093
- }
5094
-
5095
- void FastRandom::Fill(void *out_buf, Size len)
5096
- {
5097
- for (Size i = 0; i < len; i += 8) {
5098
- uint64_t rnd = Next();
5099
-
5100
- Size copy_len = std::min(RG_SIZE(rnd), len - i);
5101
- memcpy((uint8_t *)out_buf + i, &rnd, copy_len);
5102
- }
5103
- }
5104
-
5105
- int FastRandom::GetInt(int min, int max)
5106
- {
5107
- int range = max - min;
5108
- RG_ASSERT(range >= 2);
5109
-
5110
- unsigned int treshold = (UINT_MAX - UINT_MAX % range);
5111
-
5112
- unsigned int x;
5113
- do {
5114
- Fill(&x, RG_SIZE(x));
5115
- } while (x >= treshold);
5116
- x %= range;
5117
-
5118
- return min + (int)x;
5119
- }
5120
-
5121
- uint64_t FastRandom::Next()
5122
- {
5123
- // xoshiro256++ by David Blackman and Sebastiano Vigna (vigna@acm.org)
5124
- // Hopefully I did not screw it up :)
5125
-
5126
- uint64_t result = ROTL64(state[0] + state[3], 23) + state[0];
5127
- uint64_t t = state[1] << 17;
5128
-
5129
- state[2] ^= state[0];
5130
- state[3] ^= state[1];
5131
- state[1] ^= state[2];
5132
- state[0] ^= state[3];
5133
- state[2] ^= t;
5134
- state[3] = ROTL64(state[3], 45);
5135
-
5136
- return result;
5137
- }
5138
-
5139
5084
  static RG_THREAD_LOCAL Size rnd_remain;
5140
5085
  static RG_THREAD_LOCAL int64_t rnd_time;
5141
5086
  #ifndef _WIN32
@@ -5145,6 +5090,17 @@ static RG_THREAD_LOCAL uint32_t rnd_state[16];
5145
5090
  static RG_THREAD_LOCAL uint8_t rnd_buf[64];
5146
5091
  static RG_THREAD_LOCAL Size rnd_offset;
5147
5092
 
5093
+ static thread_local FastRandom rng_fast;
5094
+
5095
+ static inline uint32_t ROTL32(uint32_t v, int n)
5096
+ {
5097
+ return (v << n) | (v >> (32 - n));
5098
+ }
5099
+
5100
+ static inline uint64_t ROTL64(uint64_t v, int n) {
5101
+ return (v << n) | (v >> (64 - n));
5102
+ }
5103
+
5148
5104
  static void InitChaCha20(uint32_t state[16], uint32_t key[8], uint32_t iv[2])
5149
5105
  {
5150
5106
  alignas(uint32_t) static char str[] = "expand 32-byte k";
@@ -5251,18 +5207,18 @@ void FillRandomSafe(void *out_buf, Size len)
5251
5207
 
5252
5208
  memset(rnd_state, 0, RG_SIZE(rnd_state));
5253
5209
  #if defined(_WIN32)
5254
- RG_CRITICAL(RtlGenRandom(&buf, RG_SIZE(buf)), "RtlGenRandom() failed: %s", GetWin32ErrorString());
5210
+ RG_CRITICAL(RtlGenRandom(&buf, RG_SIZE(buf)), "RtlGenRandom() failed: %1", GetWin32ErrorString());
5255
5211
  #elif defined(__linux__)
5256
5212
  {
5257
5213
  restart:
5258
5214
  int ret = syscall(SYS_getrandom, &buf, RG_SIZE(buf), 0);
5259
- RG_CRITICAL(ret >= 0, "getentropy() failed: %s", strerror(errno));
5215
+ RG_CRITICAL(ret >= 0, "getentropy() failed: %1", strerror(errno));
5260
5216
 
5261
5217
  if (ret < RG_SIZE(buf))
5262
5218
  goto restart;
5263
5219
  }
5264
5220
  #else
5265
- RG_CRITICAL(getentropy(&buf, RG_SIZE(buf)) == 0, "getentropy() failed: %s", strerror(errno));
5221
+ RG_CRITICAL(getentropy(&buf, RG_SIZE(buf)) == 0, "getentropy() failed: %1", strerror(errno));
5266
5222
  #endif
5267
5223
 
5268
5224
  InitChaCha20(rnd_state, buf.key, buf.iv);
@@ -5312,6 +5268,75 @@ int GetRandomIntSafe(int min, int max)
5312
5268
  return min + (int)x;
5313
5269
  }
5314
5270
 
5271
+ FastRandom::FastRandom()
5272
+ {
5273
+ do {
5274
+ FillRandomSafe(state, RG_SIZE(state));
5275
+ } while (std::all_of(std::begin(state), std::end(state), [](uint64_t v) { return !v; }));
5276
+ }
5277
+
5278
+ FastRandom::FastRandom(uint64_t seed)
5279
+ {
5280
+ // splitmix64 generator to seed xoshiro256++, as recommended
5281
+
5282
+ seed += 0x9e3779b97f4a7c15;
5283
+
5284
+ for (int i = 0; i < 4; i++) {
5285
+ seed = (seed ^ (seed >> 30)) * 0xbf58476d1ce4e5b9;
5286
+ seed = (seed ^ (seed >> 27)) * 0x94d049bb133111eb;
5287
+ state[i] = seed ^ (seed >> 31);
5288
+ }
5289
+ }
5290
+
5291
+ void FastRandom::Fill(void *out_buf, Size len)
5292
+ {
5293
+ for (Size i = 0; i < len; i += 8) {
5294
+ uint64_t rnd = Next();
5295
+
5296
+ Size copy_len = std::min(RG_SIZE(rnd), len - i);
5297
+ memcpy((uint8_t *)out_buf + i, &rnd, copy_len);
5298
+ }
5299
+ }
5300
+
5301
+ int FastRandom::GetInt(int min, int max)
5302
+ {
5303
+ int range = max - min;
5304
+ RG_ASSERT(range >= 2);
5305
+
5306
+ unsigned int treshold = (UINT_MAX - UINT_MAX % range);
5307
+
5308
+ unsigned int x;
5309
+ do {
5310
+ x = (unsigned int)Next();
5311
+ } while (x >= treshold);
5312
+ x %= range;
5313
+
5314
+ return min + (int)x;
5315
+ }
5316
+
5317
+ uint64_t FastRandom::Next()
5318
+ {
5319
+ // xoshiro256++ by David Blackman and Sebastiano Vigna (vigna@acm.org)
5320
+ // Hopefully I did not screw it up :)
5321
+
5322
+ uint64_t result = ROTL64(state[0] + state[3], 23) + state[0];
5323
+ uint64_t t = state[1] << 17;
5324
+
5325
+ state[2] ^= state[0];
5326
+ state[3] ^= state[1];
5327
+ state[1] ^= state[2];
5328
+ state[0] ^= state[3];
5329
+ state[2] ^= t;
5330
+ state[3] = ROTL64(state[3], 45);
5331
+
5332
+ return result;
5333
+ }
5334
+
5335
+ int GetRandomIntFast(int min, int max)
5336
+ {
5337
+ return rng_fast.GetInt(min, max);
5338
+ }
5339
+
5315
5340
  // ------------------------------------------------------------------------
5316
5341
  // Sockets
5317
5342
  // ------------------------------------------------------------------------
@@ -5517,7 +5542,10 @@ struct Task {
5517
5542
  std::function<bool()> func;
5518
5543
  };
5519
5544
 
5520
- struct TaskQueue {
5545
+ struct WorkerData {
5546
+ AsyncPool *pool = nullptr;
5547
+ int idx;
5548
+
5521
5549
  std::mutex queue_mutex;
5522
5550
  BucketArray<Task> tasks;
5523
5551
  };
@@ -5533,15 +5561,13 @@ class AsyncPool {
5533
5561
  int refcount = 0;
5534
5562
 
5535
5563
  int async_count = 0;
5536
- HeapArray<bool> workers_state;
5537
-
5538
- HeapArray<TaskQueue> queues;
5564
+ HeapArray<WorkerData> workers;
5539
5565
  std::atomic_int pending_tasks { 0 };
5540
5566
 
5541
5567
  public:
5542
5568
  AsyncPool(int threads, bool leak);
5543
5569
 
5544
- int GetWorkerCount() const { return (int)queues.len; }
5570
+ int GetWorkerCount() const { return (int)workers.len; }
5545
5571
  int CountPendingTasks() const { return pending_tasks; }
5546
5572
 
5547
5573
  void RegisterAsync();
@@ -5552,7 +5578,7 @@ public:
5552
5578
  void RunWorker(int worker_idx);
5553
5579
  void SyncOn(Async *async);
5554
5580
 
5555
- void RunTasks(int queue_idx);
5581
+ void RunTasks(int worker_idx);
5556
5582
  void RunTask(Task *task);
5557
5583
  };
5558
5584
 
@@ -5643,25 +5669,72 @@ AsyncPool::AsyncPool(int threads, bool leak)
5643
5669
  threads = RG_ASYNC_MAX_THREADS;
5644
5670
  }
5645
5671
 
5646
- // The first queue is for the main thread, whereas workers_state[0] is
5647
- // not used but it's easier to index it the same way.
5648
- workers_state.AppendDefault(threads);
5649
- queues.AppendDefault(threads);
5672
+ // The first queue is for the main thread
5673
+ workers.AppendDefault(threads);
5650
5674
 
5651
5675
  refcount = leak;
5652
5676
  }
5653
5677
 
5678
+ #ifdef _WIN32
5679
+
5680
+ static DWORD WINAPI RunWorkerWin32(void *udata)
5681
+ {
5682
+ WorkerData *worker = (WorkerData *)udata;
5683
+ worker->pool->RunWorker(worker->idx);
5684
+ return 0;
5685
+ }
5686
+
5687
+ #else
5688
+
5689
+ static void *RunWorkerPthread(void *udata)
5690
+ {
5691
+ WorkerData *worker = (WorkerData *)udata;
5692
+ worker->pool->RunWorker(worker->idx);
5693
+ return nullptr;
5694
+ }
5695
+
5696
+ #endif
5697
+
5654
5698
  void AsyncPool::RegisterAsync()
5655
5699
  {
5656
5700
  std::lock_guard<std::mutex> lock_pool(pool_mutex);
5657
5701
 
5658
5702
  if (!async_count++) {
5659
- for (int i = 1; i < workers_state.len; i++) {
5660
- if (!workers_state[i]) {
5661
- std::thread(&AsyncPool::RunWorker, this, i).detach();
5703
+ for (int i = 1; i < workers.len; i++) {
5704
+ WorkerData *worker = &workers[i];
5705
+
5706
+ if (!worker->pool) {
5707
+ worker->pool = this;
5708
+ worker->idx = i;
5709
+
5710
+ #ifdef _WIN32
5711
+ // Our worker threads may exit after main() has returned (or exit has been called),
5712
+ // which can trigger crashes in _Cnd_do_broadcast_at_thread_exit() because it
5713
+ // tries to dereference destroyed stuff. It turns out that std::thread calls this
5714
+ // function, and we don't want that, so avoid std::thread on Windows.
5715
+ HANDLE h = CreateThread(nullptr, 0, RunWorkerWin32, worker, 0, nullptr);
5716
+ if (!h) [[unlikely]] {
5717
+ LogError("Failed to create worker thread: %1", GetWin32ErrorString());
5718
+
5719
+ worker->pool = nullptr;
5720
+ return;
5721
+ }
5722
+
5723
+ CloseHandle(h);
5724
+ #else
5725
+ pthread_t thread;
5726
+ int ret = pthread_create(&thread, nullptr, RunWorkerPthread, worker);
5727
+ if (ret) [[unlikely]] {
5728
+ LogError("Failed to create worker thread: %1", strerror(ret));
5729
+
5730
+ worker->pool = nullptr;
5731
+ return;
5732
+ }
5733
+
5734
+ pthread_detach(thread);
5735
+ #endif
5662
5736
 
5663
5737
  refcount++;
5664
- workers_state[i] = true;
5665
5738
  }
5666
5739
  }
5667
5740
  }
@@ -5677,20 +5750,20 @@ void AsyncPool::AddTask(Async *async, const std::function<bool()> &func)
5677
5750
  {
5678
5751
  if (async_running_pool != async->pool) {
5679
5752
  for (;;) {
5680
- int idx = GetRandomIntSafe(0, queues.len);
5681
- TaskQueue *queue = &queues[idx];
5753
+ int idx = GetRandomIntFast(0, (int)workers.len);
5754
+ WorkerData *worker = &workers[idx];
5682
5755
 
5683
- std::unique_lock<std::mutex> lock_queue(queue->queue_mutex, std::try_to_lock);
5756
+ std::unique_lock<std::mutex> lock_queue(worker->queue_mutex, std::try_to_lock);
5684
5757
  if (lock_queue.owns_lock()) {
5685
- queue->tasks.Append({ async, func });
5758
+ worker->tasks.Append({ async, func });
5686
5759
  break;
5687
5760
  }
5688
5761
  }
5689
5762
  } else {
5690
- TaskQueue *queue = &queues[async_running_worker_idx];
5763
+ WorkerData *worker = &workers[async_running_worker_idx];
5691
5764
 
5692
- std::lock_guard<std::mutex> lock_queue(queue->queue_mutex);
5693
- queue->tasks.Append({ async, func });
5765
+ std::lock_guard<std::mutex> lock_queue(worker->queue_mutex);
5766
+ worker->tasks.Append({ async, func });
5694
5767
  }
5695
5768
 
5696
5769
  async->remaining_tasks++;
@@ -5720,7 +5793,8 @@ void AsyncPool::RunWorker(int worker_idx)
5720
5793
  pending_cv.wait_for(lock_pool, duration, [&]() { return !!pending_tasks; });
5721
5794
  }
5722
5795
 
5723
- workers_state[worker_idx] = false;
5796
+ workers[worker_idx].pool = nullptr;
5797
+
5724
5798
  if (!--refcount) {
5725
5799
  lock_pool.unlock();
5726
5800
  delete this;
@@ -5739,31 +5813,31 @@ void AsyncPool::SyncOn(Async *async)
5739
5813
  async_running_worker_idx = 0;
5740
5814
 
5741
5815
  while (async->remaining_tasks) {
5742
- RunTasks(async_running_worker_idx);
5816
+ RunTasks(0);
5743
5817
 
5744
5818
  std::unique_lock<std::mutex> lock_sync(pool_mutex);
5745
5819
  sync_cv.wait(lock_sync, [&]() { return pending_tasks || !async->remaining_tasks; });
5746
5820
  }
5747
5821
  }
5748
5822
 
5749
- void AsyncPool::RunTasks(int queue_idx)
5823
+ void AsyncPool::RunTasks(int worker_idx)
5750
5824
  {
5751
5825
  // The '12' factor is pretty arbitrary, don't try to find meaning there
5752
- for (int i = 0; i < workers_state.len * 12; i++) {
5753
- TaskQueue *queue = &queues[queue_idx];
5754
- std::unique_lock<std::mutex> lock_queue(queue->queue_mutex, std::try_to_lock);
5826
+ for (int i = 0; i < workers.len * 12; i++) {
5827
+ WorkerData *worker = &workers[worker_idx];
5828
+ std::unique_lock<std::mutex> lock_queue(worker->queue_mutex, std::try_to_lock);
5755
5829
 
5756
- if (lock_queue.owns_lock() && queue->tasks.len) {
5757
- Task task = std::move(queue->tasks[0]);
5830
+ if (lock_queue.owns_lock() && worker->tasks.len) {
5831
+ Task task = std::move(worker->tasks[0]);
5758
5832
 
5759
- queue->tasks.RemoveFirst();
5760
- queue->tasks.Trim();
5833
+ worker->tasks.RemoveFirst();
5834
+ worker->tasks.Trim();
5761
5835
 
5762
5836
  lock_queue.unlock();
5763
5837
 
5764
5838
  RunTask(&task);
5765
5839
  } else {
5766
- queue_idx = (++queue_idx < queues.len) ? queue_idx : 0;
5840
+ worker_idx = (++worker_idx < workers.len) ? worker_idx : 0;
5767
5841
  }
5768
5842
  }
5769
5843
  }
@@ -5982,10 +6056,16 @@ void Fiber::FiberCallback(unsigned int high, unsigned int low)
5982
6056
  static RG_THREAD_LOCAL std::unique_lock<std::mutex> *fib_lock;
5983
6057
  static RG_THREAD_LOCAL Fiber *fib_self;
5984
6058
 
5985
- Fiber::Fiber(const std::function<bool()> &f, Size stack_size)
6059
+ Fiber::Fiber(const std::function<bool()> &f, Size)
5986
6060
  : f(f)
5987
6061
  {
5988
- thread = std::thread(ThreadCallback, this);
6062
+ int ret = pthread_create(&thread, nullptr, ThreadCallback, this);
6063
+ if (ret) {
6064
+ LogError("Failed to create thread: %1", strerror(ret));
6065
+ return;
6066
+ }
6067
+ joinable = true;
6068
+
5989
6069
  done = false;
5990
6070
 
5991
6071
  while (toggle == 1) {
@@ -5998,8 +6078,8 @@ Fiber::~Fiber()
5998
6078
  // We are forced to execute it until the end
5999
6079
  Finalize();
6000
6080
 
6001
- if (thread.joinable()) {
6002
- thread.join();
6081
+ if (joinable) {
6082
+ pthread_join(thread, nullptr);
6003
6083
  }
6004
6084
  }
6005
6085
 
@@ -6030,7 +6110,7 @@ bool Fiber::SwitchBack()
6030
6110
  }
6031
6111
  }
6032
6112
 
6033
- void Fiber::ThreadCallback(void *udata)
6113
+ void *Fiber::ThreadCallback(void *udata)
6034
6114
  {
6035
6115
  Fiber *self = (Fiber *)udata;
6036
6116
 
@@ -6047,6 +6127,8 @@ void Fiber::ThreadCallback(void *udata)
6047
6127
 
6048
6128
  self->toggle = 0;
6049
6129
  self->cv.notify_one();
6130
+
6131
+ return nullptr;
6050
6132
  }
6051
6133
 
6052
6134
  void Fiber::Toggle(int to, std::unique_lock<std::mutex> *lock)
@@ -3853,9 +3853,6 @@ Size ConvertWin32WideToUtf8(const wchar_t *str_w, Span<char> out_str);
3853
3853
  char *GetWin32ErrorString(uint32_t error_code = UINT32_MAX);
3854
3854
  #endif
3855
3855
 
3856
- void SetEnvironmentVar(const char *name, const char *value);
3857
- void DeleteEnvironmentVar(const char *name);
3858
-
3859
3856
  static inline bool IsPathSeparator(char c)
3860
3857
  {
3861
3858
  #ifdef _WIN32
@@ -4055,17 +4052,29 @@ bool CreatePipe(int pfd[2]);
4055
4052
  void CloseDescriptorSafe(int *fd_ptr);
4056
4053
  #endif
4057
4054
 
4058
- bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4055
+ struct ExecuteInfo {
4056
+ struct KeyValue {
4057
+ const char *key;
4058
+ const char *value;
4059
+ };
4060
+
4061
+ const char *work_dir = nullptr;
4062
+
4063
+ bool reset_env = false;
4064
+ Span<const KeyValue> env_variables = {};
4065
+ };
4066
+
4067
+ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
4059
4068
  FunctionRef<Span<const uint8_t>()> in_func,
4060
4069
  FunctionRef<void(Span<uint8_t> buf)> out_func, int *out_code);
4061
- bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4070
+ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
4062
4071
  Span<const uint8_t> in_buf, Size max_len,
4063
4072
  HeapArray<uint8_t> *out_buf, int *out_code);
4064
4073
 
4065
4074
  // Simple variants
4066
- static inline bool ExecuteCommandLine(const char *cmd_line, const char *work_dir, int *out_code)
4067
- { return ExecuteCommandLine(cmd_line, work_dir, {}, {}, out_code); }
4068
- static inline bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4075
+ static inline bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,int *out_code)
4076
+ { return ExecuteCommandLine(cmd_line, info, {}, {}, out_code); }
4077
+ static inline bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
4069
4078
  Span<const uint8_t> in_buf,
4070
4079
  FunctionRef<void(Span<uint8_t> buf)> out_func, int *out_code)
4071
4080
  {
@@ -4075,22 +4084,22 @@ static inline bool ExecuteCommandLine(const char *cmd_line, const char *work_dir
4075
4084
  return buf;
4076
4085
  };
4077
4086
 
4078
- return ExecuteCommandLine(cmd_line, work_dir, read_once, out_func, out_code);
4087
+ return ExecuteCommandLine(cmd_line, info, read_once, out_func, out_code);
4079
4088
  }
4080
4089
 
4081
4090
  // Char variants
4082
- static inline bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4091
+ static inline bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
4083
4092
  Span<const char> in_buf,
4084
4093
  FunctionRef<void(Span<char> buf)> out_func, int *out_code)
4085
4094
  {
4086
4095
  const auto write = [&](Span<uint8_t> buf) { out_func(buf.As<char>()); };
4087
- return ExecuteCommandLine(cmd_line, work_dir, in_buf.As<const uint8_t>(), write, out_code);
4096
+ return ExecuteCommandLine(cmd_line, info, in_buf.As<const uint8_t>(), write, out_code);
4088
4097
  }
4089
- static inline bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4098
+ static inline bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
4090
4099
  Span<const char> in_buf, Size max_len,
4091
4100
  HeapArray<char> *out_buf, int *out_code)
4092
4101
  {
4093
- return ExecuteCommandLine(cmd_line, work_dir, in_buf.As<const uint8_t>(), max_len,
4102
+ return ExecuteCommandLine(cmd_line, info, in_buf.As<const uint8_t>(), max_len,
4094
4103
  (HeapArray<uint8_t> *)out_buf, out_code);
4095
4104
  }
4096
4105
 
@@ -4158,6 +4167,11 @@ const char *CreateUniqueDirectory(Span<const char> directory, const char *prefix
4158
4167
  // Random
4159
4168
  // ------------------------------------------------------------------------
4160
4169
 
4170
+ void ZeroMemorySafe(void *ptr, Size len);
4171
+ void FillRandomSafe(void *buf, Size len);
4172
+ static inline void FillRandomSafe(Span<uint8_t> buf) { FillRandomSafe(buf.ptr, buf.len); }
4173
+ int GetRandomIntSafe(int min, int max);
4174
+
4161
4175
  class FastRandom {
4162
4176
  uint64_t state[4];
4163
4177
 
@@ -4190,10 +4204,7 @@ public:
4190
4204
  int operator()() { return rng.GetInt(Min, Max); }
4191
4205
  };
4192
4206
 
4193
- void ZeroMemorySafe(void *ptr, Size len);
4194
- void FillRandomSafe(void *buf, Size len);
4195
- static inline void FillRandomSafe(Span<uint8_t> buf) { FillRandomSafe(buf.ptr, buf.len); }
4196
- int GetRandomIntSafe(int min, int max);
4207
+ int GetRandomIntFast(int min, int max);
4197
4208
 
4198
4209
  // ------------------------------------------------------------------------
4199
4210
  // Sockets
@@ -4269,7 +4280,8 @@ class Fiber {
4269
4280
  #elif defined(RG_FIBER_USE_UCONTEXT)
4270
4281
  ucontext_t ucp = {};
4271
4282
  #else
4272
- std::thread thread;
4283
+ pthread_t thread;
4284
+ bool joinable = false;
4273
4285
 
4274
4286
  std::mutex mutex;
4275
4287
  std::condition_variable cv;
@@ -4297,7 +4309,7 @@ private:
4297
4309
  #elif defined(RG_FIBER_USE_UCONTEXT)
4298
4310
  static void FiberCallback(unsigned int high, unsigned int low);
4299
4311
  #else
4300
- static void ThreadCallback(void *udata);
4312
+ static void *ThreadCallback(void *udata);
4301
4313
  void Toggle(int to, std::unique_lock<std::mutex> *lock);
4302
4314
  #endif
4303
4315
  };
@@ -31,17 +31,17 @@ find_package(CNoke)
31
31
  set(CMAKE_CXX_STANDARD 20)
32
32
  if(MSVC)
33
33
  set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
34
-
35
- add_compile_options(/Zc:__cplusplus /W4 /wd4200 /wd4458 /wd4706 /wd4100 /wd4127 /wd4702 /wd4201 /wd4324)
34
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /W4 /wd4200 /wd4201 /wd4127 /wd4458 /wd4706 /wd4702 /wd4324")
36
35
 
37
36
  # ASM_MASM does not (yet) work on Windows ARM64
38
37
  if(NOT CMAKE_GENERATOR_PLATFORM MATCHES "ARM64")
39
38
  enable_language(ASM_MASM)
40
39
  endif()
41
40
  else()
42
- add_compile_options(-Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Wswitch -Werror=switch)
41
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Wswitch -Werror=switch")
42
+
43
43
  if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
44
- add_compile_options(-Wno-unknown-warning-option)
44
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option")
45
45
  endif()
46
46
  endif()
47
47
 
@@ -132,16 +132,16 @@ if(NOT MSVC OR CMAKE_C_COMPILER_ID MATCHES "[Cc]lang")
132
132
  # Restore C/C++ compiler sanity
133
133
 
134
134
  if(NOT MSVC)
135
- target_compile_options(koffi PRIVATE -fno-exceptions -fno-strict-aliasing -fwrapv
136
- -fno-delete-null-pointer-checks)
135
+ target_compile_options(koffi PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions -fno-strict-aliasing -fwrapv
136
+ -fno-delete-null-pointer-checks>)
137
137
  else()
138
- target_compile_options(koffi PRIVATE -fno-strict-aliasing /clang:-fwrapv
139
- -fno-delete-null-pointer-checks)
138
+ target_compile_options(koffi PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fno-strict-aliasing /clang:-fwrapv
139
+ -fno-delete-null-pointer-checks>)
140
140
  endif()
141
141
 
142
142
  check_cxx_compiler_flag(-fno-finite-loops use_no_finite_loops)
143
143
  if(use_no_finite_loops)
144
- target_compile_options(koffi PRIVATE -fno-finite-loops)
144
+ target_compile_options(koffi PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fno-finite-loops>)
145
145
  endif()
146
146
  endif()
147
147
  enable_unity_build(koffi)
@@ -2224,7 +2224,7 @@ static Napi::Value ResetKoffi(const Napi::CallbackInfo &info)
2224
2224
  return env.Undefined();
2225
2225
  }
2226
2226
 
2227
- static InstanceData *CreateInstance(Napi::Env env)
2227
+ static InstanceData *CreateInstance()
2228
2228
  {
2229
2229
  InstanceData *instance = new InstanceData();
2230
2230
  RG_DEFER_N(err_guard) { delete instance; };
@@ -2247,7 +2247,7 @@ static InstanceData *CreateInstance(Napi::Env env)
2247
2247
 
2248
2248
  static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
2249
2249
  {
2250
- InstanceData *instance = CreateInstance(env);
2250
+ InstanceData *instance = CreateInstance();
2251
2251
  RG_CRITICAL(instance, "Failed to initialize Koffi");
2252
2252
 
2253
2253
  env.SetInstanceData(instance);
@@ -79,7 +79,7 @@ static bool ReadAt(HANDLE h, int32_t offset, void *buf, int len)
79
79
  return false;
80
80
  if (!ReadFile(h, buf, (DWORD)len, &read, &ov))
81
81
  return false;
82
- if (read != len)
82
+ if (read != (DWORD)len)
83
83
  return false;
84
84
 
85
85
  return true;