koffi 2.5.0-beta.2 → 2.5.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 (50) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/build/2.5.1/koffi_darwin_arm64/koffi.node +0 -0
  3. package/build/{2.5.0-beta.2 → 2.5.1}/koffi_darwin_x64/koffi.node +0 -0
  4. package/build/{2.5.0-beta.2 → 2.5.1}/koffi_freebsd_arm64/koffi.node +0 -0
  5. package/build/{2.5.0-beta.2 → 2.5.1}/koffi_freebsd_ia32/koffi.node +0 -0
  6. package/build/{2.5.0-beta.2 → 2.5.1}/koffi_freebsd_x64/koffi.node +0 -0
  7. package/build/2.5.1/koffi_linux_arm32hf/koffi.node +0 -0
  8. package/build/2.5.1/koffi_linux_arm64/koffi.node +0 -0
  9. package/build/{2.5.0-beta.2 → 2.5.1}/koffi_linux_ia32/koffi.node +0 -0
  10. package/build/{2.5.0-beta.2 → 2.5.1}/koffi_linux_riscv64hf64/koffi.node +0 -0
  11. package/build/{2.5.0-beta.2 → 2.5.1}/koffi_linux_x64/koffi.node +0 -0
  12. package/build/{2.5.0-beta.2 → 2.5.1}/koffi_openbsd_ia32/koffi.node +0 -0
  13. package/build/{2.5.0-beta.2 → 2.5.1}/koffi_openbsd_x64/koffi.node +0 -0
  14. package/build/2.5.1/koffi_win32_arm64/koffi.node +0 -0
  15. package/build/{2.5.0-beta.2 → 2.5.1}/koffi_win32_ia32/koffi.node +0 -0
  16. package/build/{2.5.0-beta.2 → 2.5.1}/koffi_win32_x64/koffi.node +0 -0
  17. package/doc/functions.md +1 -1
  18. package/doc/unions.md +3 -3
  19. package/package.json +2 -2
  20. package/src/core/libcc/brotli.cc +1 -1
  21. package/src/core/libcc/libcc.cc +53 -65
  22. package/src/core/libcc/libcc.hh +67 -28
  23. package/src/core/libcc/miniz.cc +2 -2
  24. package/src/koffi/CMakeLists.txt +1 -5
  25. package/src/koffi/src/abi_arm32.cc +35 -35
  26. package/src/koffi/src/abi_arm64.cc +34 -34
  27. package/src/koffi/src/abi_riscv64.cc +29 -29
  28. package/src/koffi/src/abi_x64_sysv.cc +29 -28
  29. package/src/koffi/src/abi_x64_win.cc +23 -23
  30. package/src/koffi/src/abi_x86.cc +27 -27
  31. package/src/koffi/src/call.cc +62 -69
  32. package/src/koffi/src/call.hh +2 -2
  33. package/src/koffi/src/ffi.cc +24 -24
  34. package/src/koffi/src/ffi.hh +4 -4
  35. package/src/koffi/src/parser.cc +1 -1
  36. package/src/koffi/src/trampolines/prototypes.inc +1 -1
  37. package/src/koffi/src/util.cc +7 -7
  38. package/src/koffi/src/util.hh +1 -1
  39. package/src/koffi/src/win32.hh +4 -4
  40. package/build/2.5.0-beta.2/koffi_darwin_arm64/koffi.node +0 -0
  41. package/build/2.5.0-beta.2/koffi_linux_arm32hf/koffi.node +0 -0
  42. package/build/2.5.0-beta.2/koffi_linux_arm64/koffi.node +0 -0
  43. package/build/2.5.0-beta.2/koffi_win32_arm64/koffi.node +0 -0
  44. package/build/2.5.0-beta.2/koffi_win32_x64/koffi.pdb +0 -0
  45. /package/build/{2.5.0-beta.2 → 2.5.1}/koffi_win32_arm64/koffi.exp +0 -0
  46. /package/build/{2.5.0-beta.2 → 2.5.1}/koffi_win32_arm64/koffi.lib +0 -0
  47. /package/build/{2.5.0-beta.2 → 2.5.1}/koffi_win32_ia32/koffi.exp +0 -0
  48. /package/build/{2.5.0-beta.2 → 2.5.1}/koffi_win32_ia32/koffi.lib +0 -0
  49. /package/build/{2.5.0-beta.2 → 2.5.1}/koffi_win32_x64/koffi.exp +0 -0
  50. /package/build/{2.5.0-beta.2 → 2.5.1}/koffi_win32_x64/koffi.lib +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## Version history
4
4
 
5
+ ### Koffi 2.5
6
+
7
+ #### Koffi 2.5.1
8
+
9
+ **Main changes:**
10
+
11
+ - Fix crash with some struct types in System V 64 ABI
12
+ - Always use direct passthrough for buffer arguments
13
+
14
+ #### Koffi 2.5.0
15
+
16
+ **New features:**
17
+
18
+ - Support [union types](unions.md)
19
+
20
+ **Other fixes:**
21
+
22
+ - Fix ABI for single-float aggregate return on i386 BSD systems
23
+ - Don't mess with Node.js signal handling on POSIX systems
24
+
5
25
  ### Koffi 2.4
6
26
 
7
27
  #### Koffi 2.4.2
package/doc/functions.md CHANGED
@@ -9,7 +9,7 @@ const koffi = require('koffi');
9
9
  const lib = koffi.load('/path/to/shared/library'); // File extension depends on platforms: .so, .dll, .dylib, etc.
10
10
  ````
11
11
 
12
- This library will be automatically unloaded once all references to it (including all the functions that use it, as described below).
12
+ This library will be automatically unloaded once all references to it are gone (including all the functions that use it, as described below).
13
13
 
14
14
  Starting with *Koffi 2.3.20*, you can explicitly unload a library by calling `lib.unload()`. Any attempt to find or call a function from this library after unloading it will crash.
15
15
 
package/doc/unions.md CHANGED
@@ -104,15 +104,15 @@ const INPUT = koffi.struct('INPUT', {
104
104
  })
105
105
  });
106
106
 
107
- const SendInput = user32.func('unsigned int __stdcall SendInput(unsigned int cInputs, INPUT *pInputs, int cbSize');
107
+ const SendInput = user32.func('unsigned int __stdcall SendInput(unsigned int cInputs, INPUT *pInputs, int cbSize)');
108
108
 
109
109
  // Show/hide desktop with Win+D shortcut
110
110
 
111
111
  let events = [
112
- make_keyboard_event(VK_WIN, true),
112
+ make_keyboard_event(VK_LWIN, true),
113
113
  make_keyboard_event(VK_D, true),
114
114
  make_keyboard_event(VK_D, false),
115
- make_keyboard_event(VK_WIN, false)
115
+ make_keyboard_event(VK_LWIN, false)
116
116
  ];
117
117
 
118
118
  SendInput(events.length, events, koffi.sizeof(INPUT));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.5.0-beta.2",
4
- "stable": "2.4.2",
3
+ "version": "2.5.1",
4
+ "stable": "2.5.1",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -130,7 +130,7 @@ bool BrotliCompressor::Init(CompressionType, CompressionSpeed speed)
130
130
  if (!state)
131
131
  throw std::bad_alloc();
132
132
 
133
- RG_STATIC_ASSERT(BROTLI_MIN_QUALITY == 0 && BROTLI_MAX_QUALITY == 11);
133
+ static_assert(BROTLI_MIN_QUALITY == 0 && BROTLI_MAX_QUALITY == 11);
134
134
 
135
135
  switch (speed) {
136
136
  case CompressionSpeed::Default: { BrotliEncoderSetParameter(state, BROTLI_PARAM_QUALITY, 6); } break;
@@ -21,7 +21,7 @@
21
21
 
22
22
  #include "libcc.hh"
23
23
 
24
- #if __has_include("vendor/dragonbox/include/dragonbox/dragonbox.h") && __cplusplus >= 201703L
24
+ #if __has_include("vendor/dragonbox/include/dragonbox/dragonbox.h")
25
25
  #include "vendor/dragonbox/include/dragonbox/dragonbox.h"
26
26
  #endif
27
27
 
@@ -445,13 +445,13 @@ LocalDate LocalDate::Parse(Span<const char> date_str, unsigned int flags,
445
445
  int digit = c - '0';
446
446
  if ((unsigned int)digit < 10) {
447
447
  parts[i] = (parts[i] * 10) + digit;
448
- if (RG_UNLIKELY(++lengths[i] > 5))
448
+ if (++lengths[i] > 5) [[unlikely]]
449
449
  goto malformed;
450
450
  } else if (!lengths[i] && c == '-' && mult == 1 && i != 1) {
451
451
  mult = -1;
452
- } else if (RG_UNLIKELY(i == 2 && !(flags & (int)ParseFlag::End) && c != '/' && c != '-')) {
452
+ } else if (i == 2 && !(flags & (int)ParseFlag::End) && c != '/' && c != '-') [[unlikely]] {
453
453
  break;
454
- } else if (RG_UNLIKELY(!lengths[i] || (c != '/' && c != '-'))) {
454
+ } else if (!lengths[i] || (c != '/' && c != '-')) [[unlikely]] {
455
455
  goto malformed;
456
456
  } else {
457
457
  offset++;
@@ -464,9 +464,9 @@ LocalDate LocalDate::Parse(Span<const char> date_str, unsigned int flags,
464
464
  if ((flags & (int)ParseFlag::End) && offset < date_str.len)
465
465
  goto malformed;
466
466
 
467
- if (RG_UNLIKELY((unsigned int)lengths[1] > 2))
467
+ if ((unsigned int)lengths[1] > 2) [[unlikely]]
468
468
  goto malformed;
469
- if (RG_UNLIKELY((lengths[0] > 2) == (lengths[2] > 2))) {
469
+ if ((lengths[0] > 2) == (lengths[2] > 2)) [[unlikely]] {
470
470
  if (flags & (int)ParseFlag::Log) {
471
471
  LogError("Ambiguous date string '%1'", date_str);
472
472
  }
@@ -474,7 +474,7 @@ LocalDate LocalDate::Parse(Span<const char> date_str, unsigned int flags,
474
474
  } else if (lengths[2] > 2) {
475
475
  std::swap(parts[0], parts[2]);
476
476
  }
477
- if (RG_UNLIKELY(parts[0] < -INT16_MAX || parts[0] > INT16_MAX || (unsigned int)parts[2] > 99))
477
+ if (parts[0] < -INT16_MAX || parts[0] > INT16_MAX || (unsigned int)parts[2] > 99) [[unlikely]]
478
478
  goto malformed;
479
479
 
480
480
  date.st.year = (int16_t)parts[0];
@@ -722,13 +722,13 @@ bool CopyString(const char *str, Span<char> buf)
722
722
  #ifdef RG_DEBUG
723
723
  RG_ASSERT(buf.len > 0);
724
724
  #else
725
- if (RG_UNLIKELY(!buf.len))
725
+ if (!buf.len) [[unlikely]]
726
726
  return false;
727
727
  #endif
728
728
 
729
729
  Size i = 0;
730
730
  for (; str[i]; i++) {
731
- if (RG_UNLIKELY(i >= buf.len - 1)) {
731
+ if (i >= buf.len - 1) [[unlikely]] {
732
732
  buf[buf.len - 1] = 0;
733
733
  return false;
734
734
  }
@@ -744,11 +744,11 @@ bool CopyString(Span<const char> str, Span<char> buf)
744
744
  #ifdef RG_DEBUG
745
745
  RG_ASSERT(buf.len > 0);
746
746
  #else
747
- if (RG_UNLIKELY(!buf.len))
747
+ if (!buf.len) [[unlikely]]
748
748
  return false;
749
749
  #endif
750
750
 
751
- if (RG_UNLIKELY(str.len > buf.len - 1))
751
+ if (str.len > buf.len - 1) [[unlikely]]
752
752
  return false;
753
753
 
754
754
  memcpy_safe(buf.ptr, str.ptr, str.len);
@@ -1684,7 +1684,7 @@ static void WriteStdComplete(Span<const char> buf, FILE *fp)
1684
1684
  {
1685
1685
  while (buf.len) {
1686
1686
  Size write_len = (Size)fwrite(buf.ptr, 1, (size_t)buf.len, fp);
1687
- if (RG_UNLIKELY(!write_len))
1687
+ if (!write_len) [[unlikely]]
1688
1688
  break;
1689
1689
  buf = buf.Take(write_len, buf.len - write_len);
1690
1690
  }
@@ -1965,7 +1965,7 @@ Size ConvertUtf8ToWin32Wide(Span<const char> str, Span<wchar_t> out_str_w)
1965
1965
  {
1966
1966
  RG_ASSERT(out_str_w.len >= 2);
1967
1967
 
1968
- if (RG_UNLIKELY(!str.len)) {
1968
+ if (!str.len) [[unlikely]] {
1969
1969
  out_str_w[0] = 0;
1970
1970
  return 0;
1971
1971
  }
@@ -2230,7 +2230,7 @@ EnumResult EnumerateDirectory(const char *dirname, const char *filter, Size max_
2230
2230
  (find_data.cFileName[0] == '.' && find_data.cFileName[1] == '.' && !find_data.cFileName[2]))
2231
2231
  continue;
2232
2232
 
2233
- if (RG_UNLIKELY(count++ >= max_files && max_files >= 0)) {
2233
+ if (count++ >= max_files && max_files >= 0) [[unlikely]] {
2234
2234
  LogError("Partial enumation of directory '%1'", dirname);
2235
2235
  return EnumResult::PartialEnum;
2236
2236
  }
@@ -2292,7 +2292,7 @@ static FileType FileModeToType(mode_t mode)
2292
2292
 
2293
2293
  StatResult StatFile(const char *filename, unsigned int flags, FileInfo *out_info)
2294
2294
  {
2295
- #if defined(__linux__) && !defined(LIBCC_NO_STATX)
2295
+ #if defined(__linux__) && defined(STATX_TYPE) && !defined(LIBCC_NO_STATX)
2296
2296
  int stat_flags = (flags & (int)StatFlag::FollowSymlink) ? 0 : AT_SYMLINK_NOFOLLOW;
2297
2297
  int stat_mask = STATX_TYPE | STATX_MODE | STATX_MTIME | STATX_BTIME | STATX_SIZE;
2298
2298
 
@@ -2468,7 +2468,7 @@ EnumResult EnumerateDirectory(const char *dirname, const char *filter, Size max_
2468
2468
  continue;
2469
2469
 
2470
2470
  if (!filter || !fnmatch(filter, dent->d_name, FNM_PERIOD)) {
2471
- if (RG_UNLIKELY(count++ >= max_files && max_files >= 0)) {
2471
+ if (count++ >= max_files && max_files >= 0) [[unlikely]] {
2472
2472
  LogError("Partial enumation of directory '%1'", dirname);
2473
2473
  return EnumResult::PartialEnum;
2474
2474
  }
@@ -2738,7 +2738,7 @@ bool FindExecutableInPath(Span<const char> paths, const char *name, Allocator *a
2738
2738
  static const Span<const char> extensions[] = {".com", ".exe", ".bat", ".cmd"};
2739
2739
 
2740
2740
  for (Span<const char> ext: extensions) {
2741
- if (RG_LIKELY(ext.len < buf.Available() - 1)) {
2741
+ if (ext.len < buf.Available() - 1) [[likely]] {
2742
2742
  memcpy_safe(buf.end(), ext.ptr, ext.len + 1);
2743
2743
 
2744
2744
  if (TestFile(buf.data)) {
@@ -2750,7 +2750,7 @@ bool FindExecutableInPath(Span<const char> paths, const char *name, Allocator *a
2750
2750
  }
2751
2751
  }
2752
2752
  #else
2753
- if (RG_LIKELY(buf.len < RG_SIZE(buf.data) - 1) && TestFile(buf.data)) {
2753
+ if (buf.len < RG_SIZE(buf.data) - 1 && TestFile(buf.data)) {
2754
2754
  if (out_path) {
2755
2755
  *out_path = DuplicateString(buf.data, alloc).ptr;
2756
2756
  }
@@ -3643,7 +3643,7 @@ bool MakeDirectory(const char *directory, bool error_if_exists)
3643
3643
  bool MakeDirectoryRec(Span<const char> directory)
3644
3644
  {
3645
3645
  char buf[4096];
3646
- if (RG_UNLIKELY(directory.len >= RG_SIZE(buf))) {
3646
+ if (directory.len >= RG_SIZE(buf)) [[unlikely]] {
3647
3647
  LogError("Path '%1' is too large", directory);
3648
3648
  return false;
3649
3649
  }
@@ -4321,7 +4321,7 @@ bool ExecuteCommandLine(const char *cmd_line, Span<const uint8_t> in_buf, Size m
4321
4321
  {
4322
4322
  Size memory_max = RG_SIZE_MAX - out_buf->len - 1;
4323
4323
 
4324
- if (RG_UNLIKELY(memory_max <= 0)) {
4324
+ if (memory_max <= 0) [[unlikely]] {
4325
4325
  LogError("Exhausted memory limit");
4326
4326
  return false;
4327
4327
  }
@@ -4830,7 +4830,7 @@ static const char *CreateUniquePath(Span<const char> directory, const char *pref
4830
4830
 
4831
4831
  for (Size i = 0; i < 1000; i++) {
4832
4832
  // We want to show an error on last try
4833
- if (RG_UNLIKELY(i == 999)) {
4833
+ if (i == 999) [[unlikely]] {
4834
4834
  PopLogFilter();
4835
4835
  log_guard.Disable();
4836
4836
  }
@@ -5664,7 +5664,7 @@ Fiber::~Fiber()
5664
5664
 
5665
5665
  void Fiber::SwitchTo()
5666
5666
  {
5667
- if (RG_UNLIKELY(!fiber))
5667
+ if (!fiber) [[unlikely]]
5668
5668
  return;
5669
5669
 
5670
5670
  if (!done) {
@@ -5675,7 +5675,7 @@ void Fiber::SwitchTo()
5675
5675
 
5676
5676
  bool Fiber::Finalize()
5677
5677
  {
5678
- if (RG_UNLIKELY(!fiber))
5678
+ if (!fiber) [[unlikely]]
5679
5679
  return false;
5680
5680
 
5681
5681
  if (!done) {
@@ -5746,7 +5746,7 @@ Fiber::~Fiber()
5746
5746
 
5747
5747
  void Fiber::SwitchTo()
5748
5748
  {
5749
- if (RG_UNLIKELY(!ucp.uc_stack.ss_sp))
5749
+ if (!ucp.uc_stack.ss_sp) [[unlikely]]
5750
5750
  return;
5751
5751
 
5752
5752
  if (!done) {
@@ -5757,7 +5757,7 @@ void Fiber::SwitchTo()
5757
5757
 
5758
5758
  bool Fiber::Finalize()
5759
5759
  {
5760
- if (RG_UNLIKELY(!ucp.uc_stack.ss_sp))
5760
+ if (!ucp.uc_stack.ss_sp) [[unlikely]]
5761
5761
  return false;
5762
5762
 
5763
5763
  if (!done) {
@@ -6025,7 +6025,7 @@ bool StreamReader::Close(bool implicit)
6025
6025
 
6026
6026
  bool StreamReader::Rewind()
6027
6027
  {
6028
- if (RG_UNLIKELY(error))
6028
+ if (error) [[unlikely]]
6029
6029
  return false;
6030
6030
 
6031
6031
  switch (source.type) {
@@ -6077,7 +6077,7 @@ int StreamReader::GetDescriptor() const
6077
6077
 
6078
6078
  Size StreamReader::Read(Span<uint8_t> out_buf)
6079
6079
  {
6080
- if (RG_UNLIKELY(error))
6080
+ if (error) [[unlikely]]
6081
6081
  return -1;
6082
6082
 
6083
6083
  Size read_len = 0;
@@ -6093,7 +6093,7 @@ Size StreamReader::Read(Span<uint8_t> out_buf)
6093
6093
  eof = source.eof;
6094
6094
  }
6095
6095
 
6096
- if (RG_UNLIKELY(!error && read_max >= 0 && read_len > read_max - read_total)) {
6096
+ if (!error && read_max >= 0 && read_len > read_max - read_total) [[unlikely]] {
6097
6097
  LogError("Exceeded max stream size of %1", FmtDiskSize(read_max));
6098
6098
  error = true;
6099
6099
  return -1;
@@ -6105,7 +6105,7 @@ Size StreamReader::Read(Span<uint8_t> out_buf)
6105
6105
 
6106
6106
  Size StreamReader::ReadAll(Size max_len, HeapArray<uint8_t> *out_buf)
6107
6107
  {
6108
- if (RG_UNLIKELY(error))
6108
+ if (error) [[unlikely]]
6109
6109
  return -1;
6110
6110
 
6111
6111
  RG_DEFER_NC(buf_guard, buf_len = out_buf->len) { out_buf->RemoveFrom(buf_len); };
@@ -6114,7 +6114,7 @@ Size StreamReader::ReadAll(Size max_len, HeapArray<uint8_t> *out_buf)
6114
6114
  {
6115
6115
  Size memory_max = RG_SIZE_MAX - out_buf->len - 1;
6116
6116
 
6117
- if (RG_UNLIKELY(memory_max <= 0)) {
6117
+ if (memory_max <= 0) [[unlikely]] {
6118
6118
  LogError("Exhausted memory limit reading file '%1'", filename);
6119
6119
  return -1;
6120
6120
  }
@@ -6154,7 +6154,7 @@ Size StreamReader::ReadAll(Size max_len, HeapArray<uint8_t> *out_buf)
6154
6154
  if (read_len < 0)
6155
6155
  return -1;
6156
6156
 
6157
- if (RG_UNLIKELY(read_len > max_len - total_len)) {
6157
+ if (read_len > max_len - total_len) [[unlikely]] {
6158
6158
  LogError("File '%1' is too large (limit = %2)", filename, FmtDiskSize(max_len));
6159
6159
  return -1;
6160
6160
  }
@@ -6170,7 +6170,7 @@ Size StreamReader::ReadAll(Size max_len, HeapArray<uint8_t> *out_buf)
6170
6170
 
6171
6171
  int64_t StreamReader::ComputeRawLen()
6172
6172
  {
6173
- if (RG_UNLIKELY(error))
6173
+ if (error) [[unlikely]]
6174
6174
  return -1;
6175
6175
  if (raw_read || raw_len >= 0)
6176
6176
  return raw_len;
@@ -6289,7 +6289,7 @@ bool LineReader::Next(Span<char> *out_line)
6289
6289
  line_number = 0;
6290
6290
  return false;
6291
6291
  }
6292
- if (RG_UNLIKELY(error))
6292
+ if (error) [[unlikely]]
6293
6293
  return false;
6294
6294
 
6295
6295
  for (;;) {
@@ -6466,7 +6466,7 @@ bool StreamWriter::Open(const std::function<bool(Span<const uint8_t>)> &func, co
6466
6466
 
6467
6467
  bool StreamWriter::Flush()
6468
6468
  {
6469
- if (RG_UNLIKELY(error))
6469
+ if (error) [[unlikely]]
6470
6470
  return false;
6471
6471
 
6472
6472
  switch (dest.type) {
@@ -6504,7 +6504,7 @@ int StreamWriter::GetDescriptor() const
6504
6504
 
6505
6505
  bool StreamWriter::Write(Span<const uint8_t> buf)
6506
6506
  {
6507
- if (RG_UNLIKELY(error))
6507
+ if (error) [[unlikely]]
6508
6508
  return false;
6509
6509
 
6510
6510
  if (compressor) {
@@ -6688,7 +6688,7 @@ bool SpliceStream(StreamReader *reader, int64_t max_len, StreamWriter *writer)
6688
6688
  if (buf.len < 0)
6689
6689
  return false;
6690
6690
 
6691
- if (RG_UNLIKELY(max_len >= 0 && buf.len > max_len - total_len)) {
6691
+ if (max_len >= 0 && buf.len > max_len - total_len) [[unlikely]] {
6692
6692
  LogError("File '%1' is too large (limit = %2)", reader->GetFileName(), FmtDiskSize(max_len));
6693
6693
  return false;
6694
6694
  }
@@ -6724,7 +6724,7 @@ static bool CheckIniKey(Span<const char> key)
6724
6724
 
6725
6725
  IniParser::LineType IniParser::FindNextLine(IniProperty *out_prop)
6726
6726
  {
6727
- if (RG_UNLIKELY(error))
6727
+ if (error) [[unlikely]]
6728
6728
  return LineType::Exit;
6729
6729
 
6730
6730
  RG_DEFER_N(err_guard) { error = true; };
@@ -6921,7 +6921,7 @@ static bool assets_ready;
6921
6921
 
6922
6922
  void InitPackedMap(Span<const AssetInfo> assets)
6923
6923
  {
6924
- if (RG_LIKELY(!assets_ready)) {
6924
+ if (!assets_ready) [[likely]] {
6925
6925
  for (const AssetInfo &asset: assets) {
6926
6926
  PackedAssets_map.Set(&asset);
6927
6927
  }
@@ -6933,39 +6933,27 @@ void InitPackedMap(Span<const AssetInfo> assets)
6933
6933
  bool PatchFile(StreamReader *reader, StreamWriter *writer,
6934
6934
  FunctionRef<void(Span<const char>, StreamWriter *)> func)
6935
6935
  {
6936
- char c;
6937
- while (reader->Read(1, &c) == 1) {
6938
- if (c == '{') {
6939
- char name[33] = {};
6940
- Size name_len = reader->Read(1, &name[0]);
6941
- RG_ASSERT(name_len >= 0);
6936
+ LineReader splitter(reader);
6942
6937
 
6943
- if (IsAsciiAlpha(name[0]) || name[0] == '_') {
6944
- do {
6945
- Size read_len = reader->Read(1, &name[name_len]);
6946
- RG_ASSERT(read_len >= 0);
6938
+ Span<const char> line;
6939
+ while (splitter.Next(&line) && writer->IsValid()) {
6940
+ while (line.len) {
6941
+ Span<const char> before = SplitStr(line, "{{", &line);
6947
6942
 
6948
- if (name[name_len] == '}') {
6949
- name[name_len] = 0;
6943
+ writer->Write(before);
6950
6944
 
6951
- Span<const char> key = MakeSpan(name, name_len);
6952
- func(key, writer);
6945
+ if (before.end() < line.ptr) {
6946
+ Span<const char> expr = SplitStr(line, "}}", &line);
6953
6947
 
6954
- break;
6955
- } else if (!IsAsciiAlphaOrDigit(name[name_len]) && name[name_len] != '_') {
6956
- writer->Write('{');
6957
- writer->Write(name, name_len + 1);
6958
-
6959
- break;
6960
- }
6961
- } while (++name_len < RG_SIZE(name));
6962
- } else {
6963
- writer->Write('{');
6964
- writer->Write(name[0]);
6948
+ if (expr.end() < line.ptr) {
6949
+ func(expr, writer);
6950
+ } else {
6951
+ Print(writer, "{{%1", expr);
6952
+ }
6965
6953
  }
6966
- } else {
6967
- writer->Write(c);
6968
6954
  }
6955
+
6956
+ writer->Write('\n');
6969
6957
  }
6970
6958
 
6971
6959
  if (!reader->IsValid())