koffi 2.6.2 → 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,10 +4,18 @@
4
4
 
5
5
  ### Koffi 2.6
6
6
 
7
- #### Koffi 2.6.2 (2023-10-13)
7
+ #### Koffi 2.6.4 (2023-10-26)
8
+
9
+ - Fix build issue with recent Visual Studio versions
10
+
11
+ #### Koffi 2.6.3 (2023-10-17)
8
12
 
9
13
  - Add indirect loading script to help some bundlers
10
14
 
15
+ ```{warning}
16
+ Pre-built binaries don't work correctly in Koffi 2.6.2, skip this version.
17
+ ```
18
+
11
19
  #### Koffi 2.6.1 (2023-09-18)
12
20
 
13
21
  - Support string direction qualifiers in classic function definitions
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,13 +38,13 @@ 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
 
43
45
  Some bundlers (such as vite) don't like when require is used with native modules.
44
46
 
45
- In this case, you can use `js require('koffi/indirect')` but you will need to make sure that the native Koffi modules are packaged properly.
47
+ In this case, you can use `require('koffi/indirect')` but you will need to make sure that the native Koffi modules are packaged properly.
46
48
 
47
49
  ## Packaging examples
48
50
 
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.2",
382
- stable: "2.6.2",
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",
@@ -424,7 +424,7 @@ var require_package = __commonJS({
424
424
  cnoke: {
425
425
  output: "build/koffi/{{ platform }}_{{ arch }}",
426
426
  napi: 8,
427
- require: "./src/index.js"
427
+ require: "./index.js"
428
428
  }
429
429
  };
430
430
  }
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.2",
382
- stable: "2.6.2",
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",
@@ -424,7 +424,7 @@ var require_package = __commonJS({
424
424
  cnoke: {
425
425
  output: "build/koffi/{{ platform }}_{{ arch }}",
426
426
  napi: 8,
427
- require: "./src/index.js"
427
+ require: "./index.js"
428
428
  }
429
429
  };
430
430
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.6.2",
4
- "stable": "2.6.2",
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",
@@ -31,6 +31,6 @@
31
31
  "cnoke": {
32
32
  "output": "build/koffi/{{ platform }}_{{ arch }}",
33
33
  "napi": 8,
34
- "require": "./src/index.js"
34
+ "require": "./index.js"
35
35
  }
36
36
  }
@@ -789,10 +789,12 @@ bool ParseBool(Span<const char> str, bool *out_value, unsigned int flags,
789
789
  TRY_MATCH("1", true);
790
790
  TRY_MATCH("On", true);
791
791
  TRY_MATCH("Y", true);
792
+ TRY_MATCH("Yes", true);
792
793
  TRY_MATCH("True", true);
793
794
  TRY_MATCH("0", false);
794
795
  TRY_MATCH("Off", false);
795
796
  TRY_MATCH("N", false);
797
+ TRY_MATCH("No", false);
796
798
  TRY_MATCH("False", false);
797
799
 
798
800
  if (flags & (int)ParseFlag::Log) {
@@ -1424,7 +1426,7 @@ static inline void ProcessArg(const FmtArg &arg, AppendFunc append)
1424
1426
  RG_ASSERT(arg.u.random.len <= RG_SIZE(out_buf.data));
1425
1427
 
1426
1428
  for (Size j = 0; j < arg.u.random.len; j++) {
1427
- int rnd = GetRandomIntSafe(0, (int)chars.len);
1429
+ int rnd = GetRandomIntFast(0, (int)chars.len);
1428
1430
  out_buf.Append(chars[rnd]);
1429
1431
  }
1430
1432
 
@@ -2144,37 +2146,6 @@ fail:
2144
2146
  return str_buf;
2145
2147
  }
2146
2148
 
2147
- void SetEnvironmentVar(const char *name, const char *value)
2148
- {
2149
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2150
- RG_ASSERT(value);
2151
-
2152
- if (win32_utf8) {
2153
- RG_CRITICAL(SetEnvironmentVariableA(name, value), "Failed to set environment variable '%1' to '%2': %3", name, value, GetWin32ErrorString());
2154
- } else {
2155
- wchar_t name_w[256];
2156
- wchar_t value_w[4096];
2157
-
2158
- RG_CRITICAL(ConvertUtf8ToWin32Wide(name, name_w) >= 0, "Failed to set environment variable '%1' to '%2'", name, value);
2159
- RG_CRITICAL(ConvertUtf8ToWin32Wide(value, value_w) >= 0, "Failed to set environment variable '%1' to '%2'", name, value);
2160
- RG_CRITICAL(SetEnvironmentVariableW(name_w, value_w), "Failed to set environment variable '%1' to '%2': %3", name, value, GetWin32ErrorString());
2161
- }
2162
- }
2163
-
2164
- void DeleteEnvironmentVar(const char *name)
2165
- {
2166
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2167
-
2168
- if (win32_utf8) {
2169
- RG_CRITICAL(SetEnvironmentVariableA(name, nullptr), "Failed to clear environment variable '%1': %2", name, GetWin32ErrorString());
2170
- } else {
2171
- wchar_t name_w[256];
2172
-
2173
- RG_CRITICAL(ConvertUtf8ToWin32Wide(name, name_w) >= 0, "Failed to clear environment variable '%1'", name);
2174
- RG_CRITICAL(SetEnvironmentVariableW(name_w, nullptr), "Failed to clear environment variable '%1': %2", name, GetWin32ErrorString());
2175
- }
2176
- }
2177
-
2178
2149
  static FileType FileAttributesToType(uint32_t attr)
2179
2150
  {
2180
2151
  if (attr & FILE_ATTRIBUTE_DIRECTORY) {
@@ -2358,20 +2329,6 @@ EnumResult EnumerateDirectory(const char *dirname, const char *filter, Size max_
2358
2329
 
2359
2330
  #else
2360
2331
 
2361
- void SetEnvironmentVar(const char *name, const char *value)
2362
- {
2363
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2364
- RG_ASSERT(value);
2365
-
2366
- RG_CRITICAL(!setenv(name, value, 1), "Failed to set environment variable '%1' to '%2': %3", name, value, strerror(errno));
2367
- }
2368
-
2369
- void DeleteEnvironmentVar(const char *name)
2370
- {
2371
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2372
- RG_CRITICAL(!unsetenv(name), "Failed to clear environment variable '%1': %2", name, strerror(errno));
2373
- }
2374
-
2375
2332
  static FileType FileModeToType(mode_t mode)
2376
2333
  {
2377
2334
  if (S_ISDIR(mode)) {
@@ -3917,7 +3874,7 @@ struct PendingIO {
3917
3874
  }
3918
3875
  };
3919
3876
 
3920
- bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
3877
+ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
3921
3878
  FunctionRef<Span<const uint8_t>()> in_func,
3922
3879
  FunctionRef<void(Span<uint8_t> buf)> out_func, int *out_code)
3923
3880
  {
@@ -3932,9 +3889,9 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
3932
3889
 
3933
3890
  // Convert work directory
3934
3891
  Span<wchar_t> work_dir_w;
3935
- if (work_dir) {
3936
- work_dir_w = AllocateSpan<wchar_t>(&temp_alloc, 2 * strlen(work_dir) + 1);
3937
- 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)
3938
3895
  return false;
3939
3896
  } else {
3940
3897
  work_dir_w = {};
@@ -3986,6 +3943,45 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
3986
3943
  if (out_func.IsValid() && !CreateOverlappedPipe(true, false, PipeMode::Byte, out_pipe))
3987
3944
  return false;
3988
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
+
3989
3985
  // Start process
3990
3986
  HANDLE process_handle;
3991
3987
  {
@@ -4010,9 +4006,11 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4010
4006
  si.dwFlags |= STARTF_USESTDHANDLES;
4011
4007
  }
4012
4008
 
4009
+ int flags = CREATE_NEW_PROCESS_GROUP | CREATE_UNICODE_ENVIRONMENT;
4010
+
4013
4011
  PROCESS_INFORMATION pi = {};
4014
- if (!CreateProcessW(nullptr, cmd_line_w.ptr, nullptr, nullptr, TRUE, CREATE_NEW_PROCESS_GROUP,
4015
- 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)) {
4016
4014
  LogError("Failed to start process: %1", GetWin32ErrorString());
4017
4015
  return false;
4018
4016
  }
@@ -4221,10 +4219,12 @@ void CloseDescriptorSafe(int *fd_ptr)
4221
4219
  *fd_ptr = -1;
4222
4220
  }
4223
4221
 
4224
- bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4222
+ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
4225
4223
  FunctionRef<Span<const uint8_t>()> in_func,
4226
4224
  FunctionRef<void(Span<uint8_t> buf)> out_func, int *out_code)
4227
4225
  {
4226
+ BlockAllocator temp_alloc;
4227
+
4228
4228
  // Create read pipes
4229
4229
  int in_pfd[2] = {-1, -1};
4230
4230
  RG_DEFER {
@@ -4275,6 +4275,26 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4275
4275
  }
4276
4276
  }
4277
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
+
4278
4298
  // Start process
4279
4299
  pid_t pid;
4280
4300
  {
@@ -4295,12 +4315,12 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4295
4315
  return false;
4296
4316
  }
4297
4317
 
4298
- if (work_dir) {
4299
- const char *argv[] = {"env", "-C", work_dir, "sh", "-c", cmd_line, nullptr };
4300
- 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);
4301
4321
  } else {
4302
4322
  const char *argv[] = {"sh", "-c", cmd_line, nullptr};
4303
- 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);
4304
4324
  }
4305
4325
  if (errno) {
4306
4326
  LogError("Failed to start process: %1", strerror(errno));
@@ -4442,7 +4462,7 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4442
4462
 
4443
4463
  #endif
4444
4464
 
4445
- bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4465
+ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
4446
4466
  Span<const uint8_t> in_buf, Size max_len,
4447
4467
  HeapArray<uint8_t> *out_buf, int *out_code)
4448
4468
  {
@@ -4465,8 +4485,8 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4465
4485
  // Don't f*ck up the log
4466
4486
  bool warned = false;
4467
4487
 
4468
- bool success = ExecuteCommandLine(cmd_line, work_dir, [&]() { return in_buf; },
4469
- [&](Span<uint8_t> buf) {
4488
+ bool success = ExecuteCommandLine(cmd_line, info, [&]() { return in_buf; },
4489
+ [&](Span<uint8_t> buf) {
4470
4490
  if (out_buf->len - start_len <= max_len - buf.len) {
4471
4491
  out_buf->Append(buf);
4472
4492
  } else if (!warned) {
@@ -4492,7 +4512,7 @@ Size ReadCommandOutput(const char *cmd_line, Span<char> out_output)
4492
4512
  };
4493
4513
 
4494
4514
  int exit_code;
4495
- 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))
4496
4516
  return -1;
4497
4517
  if (exit_code) {
4498
4518
  LogDebug("Command '%1 failed (exit code: %2)", cmd_line, exit_code);
@@ -4505,7 +4525,7 @@ Size ReadCommandOutput(const char *cmd_line, Span<char> out_output)
4505
4525
  bool ReadCommandOutput(const char *cmd_line, HeapArray<char> *out_output)
4506
4526
  {
4507
4527
  int exit_code;
4508
- if (!ExecuteCommandLine(cmd_line, nullptr, {}, Mebibytes(1), out_output, &exit_code))
4528
+ if (!ExecuteCommandLine(cmd_line, {}, {}, Mebibytes(1), out_output, &exit_code))
4509
4529
  return false;
4510
4530
  if (exit_code) {
4511
4531
  LogDebug("Command '%1 failed (exit code: %2)", cmd_line, exit_code);
@@ -4757,7 +4777,7 @@ bool NotifySystemd()
4757
4777
  return false;
4758
4778
  }
4759
4779
 
4760
- DeleteEnvironmentVar("NOTIFY_SOCKET");
4780
+ unsetenv("NOTIFY_SOCKET");
4761
4781
  return true;
4762
4782
  }
4763
4783
  #endif
@@ -5061,79 +5081,6 @@ const char *CreateUniqueDirectory(Span<const char> directory, const char *prefix
5061
5081
  // Random
5062
5082
  // ------------------------------------------------------------------------
5063
5083
 
5064
- static inline uint32_t ROTL32(uint32_t v, int n)
5065
- {
5066
- return (v << n) | (v >> (32 - n));
5067
- }
5068
-
5069
- static inline uint64_t ROTL64(uint64_t v, int n) {
5070
- return (v << n) | (v >> (64 - n));
5071
- }
5072
-
5073
- FastRandom::FastRandom()
5074
- {
5075
- do {
5076
- FillRandomSafe(state, RG_SIZE(state));
5077
- } while (std::all_of(std::begin(state), std::end(state), [](uint64_t v) { return !v; }));
5078
- }
5079
-
5080
- FastRandom::FastRandom(uint64_t seed)
5081
- {
5082
- // splitmix64 generator to seed xoshiro256++, as recommended
5083
-
5084
- seed += 0x9e3779b97f4a7c15;
5085
-
5086
- for (int i = 0; i < 4; i++) {
5087
- seed = (seed ^ (seed >> 30)) * 0xbf58476d1ce4e5b9;
5088
- seed = (seed ^ (seed >> 27)) * 0x94d049bb133111eb;
5089
- state[i] = seed ^ (seed >> 31);
5090
- }
5091
- }
5092
-
5093
- void FastRandom::Fill(void *out_buf, Size len)
5094
- {
5095
- for (Size i = 0; i < len; i += 8) {
5096
- uint64_t rnd = Next();
5097
-
5098
- Size copy_len = std::min(RG_SIZE(rnd), len - i);
5099
- memcpy((uint8_t *)out_buf + i, &rnd, copy_len);
5100
- }
5101
- }
5102
-
5103
- int FastRandom::GetInt(int min, int max)
5104
- {
5105
- int range = max - min;
5106
- RG_ASSERT(range >= 2);
5107
-
5108
- unsigned int treshold = (UINT_MAX - UINT_MAX % range);
5109
-
5110
- unsigned int x;
5111
- do {
5112
- Fill(&x, RG_SIZE(x));
5113
- } while (x >= treshold);
5114
- x %= range;
5115
-
5116
- return min + (int)x;
5117
- }
5118
-
5119
- uint64_t FastRandom::Next()
5120
- {
5121
- // xoshiro256++ by David Blackman and Sebastiano Vigna (vigna@acm.org)
5122
- // Hopefully I did not screw it up :)
5123
-
5124
- uint64_t result = ROTL64(state[0] + state[3], 23) + state[0];
5125
- uint64_t t = state[1] << 17;
5126
-
5127
- state[2] ^= state[0];
5128
- state[3] ^= state[1];
5129
- state[1] ^= state[2];
5130
- state[0] ^= state[3];
5131
- state[2] ^= t;
5132
- state[3] = ROTL64(state[3], 45);
5133
-
5134
- return result;
5135
- }
5136
-
5137
5084
  static RG_THREAD_LOCAL Size rnd_remain;
5138
5085
  static RG_THREAD_LOCAL int64_t rnd_time;
5139
5086
  #ifndef _WIN32
@@ -5143,6 +5090,17 @@ static RG_THREAD_LOCAL uint32_t rnd_state[16];
5143
5090
  static RG_THREAD_LOCAL uint8_t rnd_buf[64];
5144
5091
  static RG_THREAD_LOCAL Size rnd_offset;
5145
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
+
5146
5104
  static void InitChaCha20(uint32_t state[16], uint32_t key[8], uint32_t iv[2])
5147
5105
  {
5148
5106
  alignas(uint32_t) static char str[] = "expand 32-byte k";
@@ -5249,18 +5207,18 @@ void FillRandomSafe(void *out_buf, Size len)
5249
5207
 
5250
5208
  memset(rnd_state, 0, RG_SIZE(rnd_state));
5251
5209
  #if defined(_WIN32)
5252
- RG_CRITICAL(RtlGenRandom(&buf, RG_SIZE(buf)), "RtlGenRandom() failed: %s", GetWin32ErrorString());
5210
+ RG_CRITICAL(RtlGenRandom(&buf, RG_SIZE(buf)), "RtlGenRandom() failed: %1", GetWin32ErrorString());
5253
5211
  #elif defined(__linux__)
5254
5212
  {
5255
5213
  restart:
5256
5214
  int ret = syscall(SYS_getrandom, &buf, RG_SIZE(buf), 0);
5257
- RG_CRITICAL(ret >= 0, "getentropy() failed: %s", strerror(errno));
5215
+ RG_CRITICAL(ret >= 0, "getentropy() failed: %1", strerror(errno));
5258
5216
 
5259
5217
  if (ret < RG_SIZE(buf))
5260
5218
  goto restart;
5261
5219
  }
5262
5220
  #else
5263
- 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));
5264
5222
  #endif
5265
5223
 
5266
5224
  InitChaCha20(rnd_state, buf.key, buf.iv);
@@ -5310,6 +5268,75 @@ int GetRandomIntSafe(int min, int max)
5310
5268
  return min + (int)x;
5311
5269
  }
5312
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
+
5313
5340
  // ------------------------------------------------------------------------
5314
5341
  // Sockets
5315
5342
  // ------------------------------------------------------------------------
@@ -5515,7 +5542,10 @@ struct Task {
5515
5542
  std::function<bool()> func;
5516
5543
  };
5517
5544
 
5518
- struct TaskQueue {
5545
+ struct WorkerData {
5546
+ AsyncPool *pool = nullptr;
5547
+ int idx;
5548
+
5519
5549
  std::mutex queue_mutex;
5520
5550
  BucketArray<Task> tasks;
5521
5551
  };
@@ -5531,15 +5561,13 @@ class AsyncPool {
5531
5561
  int refcount = 0;
5532
5562
 
5533
5563
  int async_count = 0;
5534
- HeapArray<bool> workers_state;
5535
-
5536
- HeapArray<TaskQueue> queues;
5564
+ HeapArray<WorkerData> workers;
5537
5565
  std::atomic_int pending_tasks { 0 };
5538
5566
 
5539
5567
  public:
5540
5568
  AsyncPool(int threads, bool leak);
5541
5569
 
5542
- int GetWorkerCount() const { return (int)queues.len; }
5570
+ int GetWorkerCount() const { return (int)workers.len; }
5543
5571
  int CountPendingTasks() const { return pending_tasks; }
5544
5572
 
5545
5573
  void RegisterAsync();
@@ -5550,7 +5578,7 @@ public:
5550
5578
  void RunWorker(int worker_idx);
5551
5579
  void SyncOn(Async *async);
5552
5580
 
5553
- void RunTasks(int queue_idx);
5581
+ void RunTasks(int worker_idx);
5554
5582
  void RunTask(Task *task);
5555
5583
  };
5556
5584
 
@@ -5641,25 +5669,72 @@ AsyncPool::AsyncPool(int threads, bool leak)
5641
5669
  threads = RG_ASYNC_MAX_THREADS;
5642
5670
  }
5643
5671
 
5644
- // The first queue is for the main thread, whereas workers_state[0] is
5645
- // not used but it's easier to index it the same way.
5646
- workers_state.AppendDefault(threads);
5647
- queues.AppendDefault(threads);
5672
+ // The first queue is for the main thread
5673
+ workers.AppendDefault(threads);
5648
5674
 
5649
5675
  refcount = leak;
5650
5676
  }
5651
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
+
5652
5698
  void AsyncPool::RegisterAsync()
5653
5699
  {
5654
5700
  std::lock_guard<std::mutex> lock_pool(pool_mutex);
5655
5701
 
5656
5702
  if (!async_count++) {
5657
- for (int i = 1; i < workers_state.len; i++) {
5658
- if (!workers_state[i]) {
5659
- 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
5660
5736
 
5661
5737
  refcount++;
5662
- workers_state[i] = true;
5663
5738
  }
5664
5739
  }
5665
5740
  }
@@ -5675,20 +5750,20 @@ void AsyncPool::AddTask(Async *async, const std::function<bool()> &func)
5675
5750
  {
5676
5751
  if (async_running_pool != async->pool) {
5677
5752
  for (;;) {
5678
- int idx = GetRandomIntSafe(0, queues.len);
5679
- TaskQueue *queue = &queues[idx];
5753
+ int idx = GetRandomIntFast(0, (int)workers.len);
5754
+ WorkerData *worker = &workers[idx];
5680
5755
 
5681
- 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);
5682
5757
  if (lock_queue.owns_lock()) {
5683
- queue->tasks.Append({ async, func });
5758
+ worker->tasks.Append({ async, func });
5684
5759
  break;
5685
5760
  }
5686
5761
  }
5687
5762
  } else {
5688
- TaskQueue *queue = &queues[async_running_worker_idx];
5763
+ WorkerData *worker = &workers[async_running_worker_idx];
5689
5764
 
5690
- std::lock_guard<std::mutex> lock_queue(queue->queue_mutex);
5691
- queue->tasks.Append({ async, func });
5765
+ std::lock_guard<std::mutex> lock_queue(worker->queue_mutex);
5766
+ worker->tasks.Append({ async, func });
5692
5767
  }
5693
5768
 
5694
5769
  async->remaining_tasks++;
@@ -5718,7 +5793,8 @@ void AsyncPool::RunWorker(int worker_idx)
5718
5793
  pending_cv.wait_for(lock_pool, duration, [&]() { return !!pending_tasks; });
5719
5794
  }
5720
5795
 
5721
- workers_state[worker_idx] = false;
5796
+ workers[worker_idx].pool = nullptr;
5797
+
5722
5798
  if (!--refcount) {
5723
5799
  lock_pool.unlock();
5724
5800
  delete this;
@@ -5737,31 +5813,31 @@ void AsyncPool::SyncOn(Async *async)
5737
5813
  async_running_worker_idx = 0;
5738
5814
 
5739
5815
  while (async->remaining_tasks) {
5740
- RunTasks(async_running_worker_idx);
5816
+ RunTasks(0);
5741
5817
 
5742
5818
  std::unique_lock<std::mutex> lock_sync(pool_mutex);
5743
5819
  sync_cv.wait(lock_sync, [&]() { return pending_tasks || !async->remaining_tasks; });
5744
5820
  }
5745
5821
  }
5746
5822
 
5747
- void AsyncPool::RunTasks(int queue_idx)
5823
+ void AsyncPool::RunTasks(int worker_idx)
5748
5824
  {
5749
5825
  // The '12' factor is pretty arbitrary, don't try to find meaning there
5750
- for (int i = 0; i < workers_state.len * 12; i++) {
5751
- TaskQueue *queue = &queues[queue_idx];
5752
- 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);
5753
5829
 
5754
- if (lock_queue.owns_lock() && queue->tasks.len) {
5755
- Task task = std::move(queue->tasks[0]);
5830
+ if (lock_queue.owns_lock() && worker->tasks.len) {
5831
+ Task task = std::move(worker->tasks[0]);
5756
5832
 
5757
- queue->tasks.RemoveFirst();
5758
- queue->tasks.Trim();
5833
+ worker->tasks.RemoveFirst();
5834
+ worker->tasks.Trim();
5759
5835
 
5760
5836
  lock_queue.unlock();
5761
5837
 
5762
5838
  RunTask(&task);
5763
5839
  } else {
5764
- queue_idx = (++queue_idx < queues.len) ? queue_idx : 0;
5840
+ worker_idx = (++worker_idx < workers.len) ? worker_idx : 0;
5765
5841
  }
5766
5842
  }
5767
5843
  }
@@ -5980,10 +6056,16 @@ void Fiber::FiberCallback(unsigned int high, unsigned int low)
5980
6056
  static RG_THREAD_LOCAL std::unique_lock<std::mutex> *fib_lock;
5981
6057
  static RG_THREAD_LOCAL Fiber *fib_self;
5982
6058
 
5983
- Fiber::Fiber(const std::function<bool()> &f, Size stack_size)
6059
+ Fiber::Fiber(const std::function<bool()> &f, Size)
5984
6060
  : f(f)
5985
6061
  {
5986
- 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
+
5987
6069
  done = false;
5988
6070
 
5989
6071
  while (toggle == 1) {
@@ -5996,8 +6078,8 @@ Fiber::~Fiber()
5996
6078
  // We are forced to execute it until the end
5997
6079
  Finalize();
5998
6080
 
5999
- if (thread.joinable()) {
6000
- thread.join();
6081
+ if (joinable) {
6082
+ pthread_join(thread, nullptr);
6001
6083
  }
6002
6084
  }
6003
6085
 
@@ -6028,7 +6110,7 @@ bool Fiber::SwitchBack()
6028
6110
  }
6029
6111
  }
6030
6112
 
6031
- void Fiber::ThreadCallback(void *udata)
6113
+ void *Fiber::ThreadCallback(void *udata)
6032
6114
  {
6033
6115
  Fiber *self = (Fiber *)udata;
6034
6116
 
@@ -6045,6 +6127,8 @@ void Fiber::ThreadCallback(void *udata)
6045
6127
 
6046
6128
  self->toggle = 0;
6047
6129
  self->cv.notify_one();
6130
+
6131
+ return nullptr;
6048
6132
  }
6049
6133
 
6050
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;