koffi 2.12.2 → 2.12.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/build/koffi/darwin_arm64/koffi.node +0 -0
  3. package/build/koffi/darwin_x64/koffi.node +0 -0
  4. package/build/koffi/freebsd_arm64/koffi.node +0 -0
  5. package/build/koffi/freebsd_ia32/koffi.node +0 -0
  6. package/build/koffi/freebsd_x64/koffi.node +0 -0
  7. package/build/koffi/linux_arm64/koffi.node +0 -0
  8. package/build/koffi/linux_armhf/koffi.node +0 -0
  9. package/build/koffi/linux_ia32/koffi.node +0 -0
  10. package/build/koffi/linux_loong64/koffi.node +0 -0
  11. package/build/koffi/linux_riscv64d/koffi.node +0 -0
  12. package/build/koffi/linux_x64/koffi.node +0 -0
  13. package/build/koffi/musl_arm64/koffi.node +0 -0
  14. package/build/koffi/musl_x64/koffi.node +0 -0
  15. package/build/koffi/openbsd_ia32/koffi.node +0 -0
  16. package/build/koffi/openbsd_x64/koffi.node +0 -0
  17. package/build/koffi/win32_arm64/koffi.node +0 -0
  18. package/build/koffi/win32_ia32/koffi.node +0 -0
  19. package/build/koffi/win32_x64/koffi.node +0 -0
  20. package/index.d.ts +226 -143
  21. package/index.js +9 -9
  22. package/indirect.js +9 -9
  23. package/package.json +2 -2
  24. package/src/core/base/base.cc +515 -78
  25. package/src/core/base/base.hh +56 -11
  26. package/src/core/base/crc.inc +2232 -0
  27. package/src/core/base/crc_gen.py +109 -0
  28. package/src/core/base/unicode.inc +426 -0
  29. package/src/core/{unicode/generate.py → base/unicode_gen.py} +84 -19
  30. package/src/koffi/CMakeLists.txt +0 -1
  31. package/src/koffi/src/ffi.cc +0 -1
  32. package/src/koffi/src/parser.cc +0 -1
  33. package/src/core/unicode/xid.cc +0 -52
  34. package/src/core/unicode/xid.hh +0 -29
  35. package/src/core/unicode/xid.inc +0 -465
@@ -20,6 +20,8 @@
20
20
  // OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  #include "base.hh"
23
+ #include "crc.inc"
24
+ #include "unicode.inc"
23
25
 
24
26
  #if __has_include("vendor/dragonbox/include/dragonbox/dragonbox.h")
25
27
  #include "vendor/dragonbox/include/dragonbox/dragonbox.h"
@@ -1854,6 +1856,35 @@ void FmtLowerAscii::Format(FunctionRef<void(Span<const char>)> append) const
1854
1856
  }
1855
1857
  }
1856
1858
 
1859
+ void FmtEscape::Format(FunctionRef<void(Span<const char>)> append) const
1860
+ {
1861
+ static const char literals[] = "0123456789ABCDEF";
1862
+
1863
+ for (char c: str) {
1864
+ if (c >= 32 && (unsigned int)c < 128) {
1865
+ append(c);
1866
+ } else {
1867
+ switch (c) {
1868
+ case '\t': { append('\t'); } break;
1869
+ case '\r': { append("\r"); } break;
1870
+ case '\n': { append("\n"); } break;
1871
+
1872
+ default: {
1873
+ char encoded[4];
1874
+
1875
+ encoded[0] = '\\';
1876
+ encoded[1] = 'x';
1877
+ encoded[2] = literals[((uint8_t)c >> 4) & 0xF];
1878
+ encoded[3] = literals[((uint8_t)c >> 0) & 0xF];
1879
+
1880
+ Span<const char> buf = MakeSpan(encoded, 4);
1881
+ append(buf);
1882
+ } break;
1883
+ }
1884
+ }
1885
+ }
1886
+ }
1887
+
1857
1888
  void FmtUrlSafe::Format(FunctionRef<void(Span<const char>)> append) const
1858
1889
  {
1859
1890
  static const char literals[] = "0123456789ABCDEF";
@@ -4473,14 +4504,44 @@ bool SpliceFile(int src_fd, const char *src_filename, int64_t src_offset,
4473
4504
  int64_t max = size;
4474
4505
  progress(0, max);
4475
4506
 
4476
- #if defined(__linux__) || defined(__FreeBSD__)
4477
4507
  // Try copy_file_range() if available
4508
+ #if defined(SYS_copy_file_range)
4478
4509
  {
4479
4510
  bool first = true;
4480
4511
 
4481
4512
  while (size) {
4513
+ // glibc < 2.27 doesn't define copy_file_range
4514
+
4482
4515
  size_t count = (size_t)std::min(size, (int64_t)Mebibytes(64));
4483
- ssize_t ret = copy_file_range(src_fd, (off_t *)&src_offset, dest_fd, (off_t *)&dest_offset, count, 0);
4516
+ ssize_t ret = syscall(SYS_copy_file_range, src_fd, (off_t *)&src_offset, dest_fd, (off_t *)&dest_offset, count, 0u);
4517
+
4518
+ if (ret < 0) {
4519
+ if (first && errno == EXDEV)
4520
+ goto xdev;
4521
+ if (errno == EINTR)
4522
+ continue;
4523
+
4524
+ LogError("Failed to copy '%1' to '%2': %3", src_filename, dest_filename, strerror(errno));
4525
+ return false;
4526
+ }
4527
+
4528
+ first = false;
4529
+ size -= ret;
4530
+
4531
+ progress(max - size, max);
4532
+ }
4533
+
4534
+ return true;
4535
+ }
4536
+
4537
+ xdev:
4538
+ #elif defined(__FreeBSD__)
4539
+ {
4540
+ bool first = true;
4541
+
4542
+ while (size) {
4543
+ size_t count = (size_t)std::min(size, (int64_t)Mebibytes(64));
4544
+ ssize_t ret = copy_file_range(src_fd, (off_t *)&src_offset, dest_fd, (off_t *)&dest_offset, count, 0u);
4484
4545
 
4485
4546
  if (ret < 0) {
4486
4547
  if (first && errno == EXDEV)
@@ -5963,16 +6024,12 @@ const char *GetTemporaryDirectory()
5963
6024
 
5964
6025
  #endif
5965
6026
 
5966
- const char *FindConfigFile(Span<const char *const> names, Allocator *alloc,
5967
- HeapArray<const char *> *out_possibilities)
6027
+ const char *FindConfigFile(const char *directory, Span<const char *const> names,
6028
+ Allocator *alloc, HeapArray<const char *> *out_possibilities)
5968
6029
  {
5969
- decltype(GetUserConfigPath) *funcs[] = {
5970
- [](const char *name, Allocator *alloc) {
5971
- Span<const char> dir = GetApplicationDirectory();
6030
+ RG_ASSERT(!directory || directory[0]);
5972
6031
 
5973
- const char *filename = Fmt(alloc, "%1%/%2", dir, name).ptr;
5974
- return filename;
5975
- },
6032
+ decltype(GetUserConfigPath) *funcs[] = {
5976
6033
  GetUserConfigPath,
5977
6034
  #if !defined(_WIN32)
5978
6035
  GetSystemConfigPath
@@ -5981,19 +6038,46 @@ const char *FindConfigFile(Span<const char *const> names, Allocator *alloc,
5981
6038
 
5982
6039
  const char *filename = nullptr;
5983
6040
 
5984
- for (const auto &func: funcs) {
6041
+ // Try application directory
6042
+ for (const char *name: names) {
6043
+ Span<const char> dir = GetApplicationDirectory();
6044
+ const char *path = Fmt(alloc, "%1%/%2", dir, name).ptr;
6045
+
6046
+ if (!filename && TestFile(path, FileType::File)) {
6047
+ filename = path;
6048
+ }
6049
+ if (out_possibilities) {
6050
+ out_possibilities->Append(path);
6051
+ }
6052
+ }
6053
+
6054
+ LocalArray<const char *, 8> tests;
6055
+ {
6056
+ RG_ASSERT(names.len <= tests.Available());
6057
+
5985
6058
  for (const char *name: names) {
5986
- const char *path = func(name, alloc);
6059
+ if (directory) {
6060
+ const char *test = Fmt(alloc, "%1%/%2", directory, name).ptr;
6061
+ tests.Append(test);
6062
+ } else {
6063
+ tests.Append(name);
6064
+ }
6065
+ }
6066
+ }
6067
+
6068
+ // Try standard paths
6069
+ for (const auto &func: funcs) {
6070
+ for (const char *test: tests) {
6071
+ const char *path = func(test, alloc);
5987
6072
 
5988
6073
  if (!path)
5989
6074
  continue;
5990
6075
 
5991
- if (TestFile(path, FileType::File)) {
6076
+ if (!filename && TestFile(path, FileType::File)) {
5992
6077
  filename = path;
5993
6078
  }
5994
6079
  if (out_possibilities) {
5995
6080
  out_possibilities->Append(path);
5996
- break;
5997
6081
  }
5998
6082
  }
5999
6083
  }
@@ -7555,6 +7639,8 @@ bool StreamReader::Rewind()
7555
7639
  }
7556
7640
 
7557
7641
  source.eof = false;
7642
+ raw_len = -1;
7643
+ raw_read = 0;
7558
7644
  eof = false;
7559
7645
 
7560
7646
  return true;
@@ -7992,6 +8078,59 @@ bool StreamWriter::Open(const std::function<bool(Span<const uint8_t>)> &func, co
7992
8078
  return true;
7993
8079
  }
7994
8080
 
8081
+ bool StreamWriter::Rewind()
8082
+ {
8083
+ if (error) [[unlikely]]
8084
+ return false;
8085
+
8086
+ if (encoder) [[unlikely]] {
8087
+ LogError("Cannot rewind stream with encoder");
8088
+ return false;
8089
+ }
8090
+
8091
+ switch (dest.type) {
8092
+ case DestinationType::Memory: { dest.u.mem.memory->RemoveFrom(dest.u.mem.start); } break;
8093
+
8094
+ case DestinationType::LineFile:
8095
+ case DestinationType::BufferedFile:
8096
+ case DestinationType::DirectFile: {
8097
+ if (lseek(dest.u.file.fd, 0, SEEK_SET) < 0) {
8098
+ LogError("Failed to rewind '%1': %2", filename, strerror(errno));
8099
+ error = true;
8100
+ return false;
8101
+ }
8102
+
8103
+ #if defined(_WIN32)
8104
+ HANDLE h = (HANDLE)_get_osfhandle(dest.u.file.fd);
8105
+
8106
+ if (!SetEndOfFile(h)) {
8107
+ LogError("Failed to truncate '%1': %2", filename, GetWin32ErrorString());
8108
+ error = true;
8109
+ return false;
8110
+ }
8111
+ #else
8112
+ if (ftruncate(dest.u.file.fd, 0) < 0) {
8113
+ LogError("Failed to truncate '%1': %2", filename, strerror(errno));
8114
+ error = true;
8115
+ return false;
8116
+ }
8117
+ #endif
8118
+
8119
+ dest.u.file.buf_used = 0;
8120
+ } break;
8121
+
8122
+ case DestinationType::Function: {
8123
+ LogError("Cannot rewind stream '%1'", filename);
8124
+ error = true;
8125
+ return false;
8126
+ } break;
8127
+ }
8128
+
8129
+ raw_written = 0;
8130
+
8131
+ return true;
8132
+ }
8133
+
7995
8134
  bool StreamWriter::Flush()
7996
8135
  {
7997
8136
  #if !defined(__wasm__)
@@ -9027,8 +9166,10 @@ bool ConsolePrompter::Read(Span<const char> *out_str)
9027
9166
  }
9028
9167
  }
9029
9168
 
9030
- bool ConsolePrompter::ReadYN(bool *out_value)
9169
+ Size ConsolePrompter::ReadEnum(Span<const PromptChoice> choices, Size value)
9031
9170
  {
9171
+ RG_ASSERT(value < choices.len);
9172
+
9032
9173
  #if !defined(_WIN32) && !defined(__wasm__)
9033
9174
  struct sigaction old_sa;
9034
9175
  IgnoreSigWinch(&old_sa);
@@ -9041,9 +9182,9 @@ bool ConsolePrompter::ReadYN(bool *out_value)
9041
9182
  DisableRawMode();
9042
9183
  };
9043
9184
 
9044
- return ReadRawYN(out_value);
9185
+ return ReadRawEnum(choices, value);
9045
9186
  } else {
9046
- return ReadBufferedYN(out_value);
9187
+ return ReadBufferedEnum(choices);
9047
9188
  }
9048
9189
  }
9049
9190
 
@@ -9070,8 +9211,8 @@ bool ConsolePrompter::ReadRaw(Span<const char> *out_str)
9070
9211
  StdErr->Flush();
9071
9212
 
9072
9213
  prompt_columns = ComputeWidth(prompt);
9073
-
9074
9214
  str_offset = str.len;
9215
+
9075
9216
  RenderRaw();
9076
9217
 
9077
9218
  int32_t uc;
@@ -9294,18 +9435,13 @@ bool ConsolePrompter::ReadRaw(Span<const char> *out_str)
9294
9435
  return true;
9295
9436
  }
9296
9437
 
9297
- bool ConsolePrompter::ReadRawYN(bool *out_value)
9438
+ Size ConsolePrompter::ReadRawEnum(Span<const PromptChoice> choices, Size value)
9298
9439
  {
9299
- const char *yn = "[Y/N]";
9300
-
9301
9440
  StdErr->Flush();
9302
9441
 
9303
- prompt_columns = ComputeWidth(prompt) + ComputeWidth(yn) + 1;
9304
-
9305
- str.RemoveFrom(0);
9306
- str_offset = 0;
9442
+ prompt_columns = 0;
9443
+ FormatChoices(choices, value);
9307
9444
  RenderRaw();
9308
- Print(StdErr, "%!D..%1%!0 ", yn);
9309
9445
 
9310
9446
  int32_t uc;
9311
9447
  while ((uc = ReadChar()) >= 0) {
@@ -9316,36 +9452,86 @@ bool ConsolePrompter::ReadRawYN(bool *out_value)
9316
9452
  }
9317
9453
 
9318
9454
  switch (uc) {
9319
- case 0x3: { // Ctrl-C
9320
- StdErr->Write("\r\n");
9321
- StdErr->Flush();
9455
+ case 0x1B: {
9456
+ LocalArray<char, 16> buf;
9322
9457
 
9323
- return false;
9324
- } break;
9325
- case 0x4: { // Ctrl-D
9326
- return false;
9458
+ const auto match_escape = [&](const char *seq) {
9459
+ RG_ASSERT(strlen(seq) < RG_SIZE(buf.data));
9460
+
9461
+ for (Size i = 0; seq[i]; i++) {
9462
+ if (i >= buf.len) {
9463
+ uc = ReadChar();
9464
+
9465
+ if (uc >= 128) {
9466
+ // Got some kind of non-ASCII character, make sure nothing else matches
9467
+ buf.Append(0);
9468
+ return false;
9469
+ }
9470
+
9471
+ buf.Append((char)uc);
9472
+ }
9473
+ if (buf[i] != seq[i])
9474
+ return false;
9475
+ }
9476
+
9477
+ return true;
9478
+ };
9479
+
9480
+ if (match_escape("[A")) { // Up
9481
+ fake_input = "\x10";
9482
+ } else if (match_escape("[B")) { // Down
9483
+ fake_input = "\x0E";
9484
+ }
9327
9485
  } break;
9328
9486
 
9329
- case 'Y':
9330
- case 'y': {
9331
- StdErr->Write("Y\n");
9487
+ case 0x3: // Ctrl-C
9488
+ case 0x4: { // Ctrl-D
9489
+ if (rows > y) {
9490
+ Print(StdErr, "\x1B[%1B", rows - y);
9491
+ }
9492
+ StdErr->Write("\r");
9332
9493
  StdErr->Flush();
9333
9494
 
9334
- *out_value = true;
9335
- return true;
9495
+ return -1;
9496
+ } break;
9497
+
9498
+ case 0xE: { // Down
9499
+ if (value + 1 < choices.len) {
9500
+ FormatChoices(choices, ++value);
9501
+ RenderRaw();
9502
+ }
9503
+ } break;
9504
+ case 0x10: { // Up
9505
+ if (value > 0) {
9506
+ FormatChoices(choices, --value);
9507
+ RenderRaw();
9508
+ }
9336
9509
  } break;
9337
- case 'N':
9338
- case 'n': {
9339
- StdErr->Write("N\n");
9510
+
9511
+ default: {
9512
+ const auto it = std::find_if(choices.begin(), choices.end(),
9513
+ [&](const PromptChoice &choice) { return choice.c == uc; });
9514
+ if (it == choices.end())
9515
+ break;
9516
+ value = it - choices.begin();
9517
+ } [[fallthrough]];
9518
+
9519
+ case '\r':
9520
+ case '\n': {
9521
+ str.RemoveFrom(0);
9522
+ str.Append(choices[value].str);
9523
+ str_offset = str.len;
9524
+ RenderRaw();
9525
+
9526
+ StdErr->Write("\r\n");
9340
9527
  StdErr->Flush();
9341
9528
 
9342
- *out_value = false;
9343
- return true;
9529
+ return value;
9344
9530
  } break;
9345
9531
  }
9346
9532
  }
9347
9533
 
9348
- return false;
9534
+ return -1;
9349
9535
  }
9350
9536
 
9351
9537
  bool ConsolePrompter::ReadBuffered(Span<const char> *out_str)
@@ -9374,41 +9560,43 @@ bool ConsolePrompter::ReadBuffered(Span<const char> *out_str)
9374
9560
  return false;
9375
9561
  }
9376
9562
 
9377
- bool ConsolePrompter::ReadBufferedYN(bool *out_value)
9563
+ Size ConsolePrompter::ReadBufferedEnum(Span<const PromptChoice> choices)
9378
9564
  {
9379
- const char *yn = "[Yes/No]";
9565
+ static const Span<const char> prefix = "Input your choice: ";
9380
9566
 
9381
- prompt_columns = ComputeWidth(prompt) + ComputeWidth(yn) + 1;
9567
+ prompt_columns = 0;
9568
+ FormatChoices(choices, 0);
9569
+ RenderBuffered();
9382
9570
 
9383
- while (!StdIn->IsEOF()) {
9384
- str.RemoveFrom(0);
9385
- str_offset = 0;
9386
- RenderBuffered();
9387
- Print(StdErr, "%1 ", yn);
9571
+ Print(StdErr, "\n%1", prefix);
9572
+ StdErr->Flush();
9388
9573
 
9389
- for (;;) {
9390
- uint8_t c = 0;
9391
- if (StdIn->Read(1, &c) < 0)
9392
- return false;
9574
+ do {
9575
+ uint8_t c = 0;
9576
+ if (StdIn->Read(1, &c) < 0)
9577
+ return -1;
9393
9578
 
9394
- if (c == '\n') {
9395
- if (TestStrI(str, "y") || TestStrI(str, "yes")) {
9396
- *out_value = true;
9397
- return true;
9398
- } else if (TestStrI(str, "n") || TestStrI(str, "no")) {
9399
- *out_value = false;
9400
- return true;
9401
- } else {
9402
- break;
9403
- }
9404
- } else if (c >= 32 || c == '\t') {
9405
- str.Append((char)c);
9579
+ if (c == '\n') {
9580
+ Span<const char> end = TrimStr(SplitStrReverse(str, '\n'));
9581
+
9582
+ if (end.len == 1) {
9583
+ const auto it = std::find_if(choices.begin(), choices.end(),
9584
+ [&](const PromptChoice &choice) { return choice.c == end[0]; });
9585
+ if (it != choices.end())
9586
+ return it - choices.ptr;
9406
9587
  }
9588
+
9589
+ str.RemoveFrom(end.ptr - str.ptr);
9590
+
9591
+ StdErr->Write(prefix);
9592
+ StdErr->Flush();
9593
+ } else if (c >= 32 || c == '\t') {
9594
+ str.Append((char)c);
9407
9595
  }
9408
- }
9596
+ } while (!StdIn->IsEOF());
9409
9597
 
9410
9598
  // EOF
9411
- return false;
9599
+ return -1;
9412
9600
  }
9413
9601
 
9414
9602
  void ConsolePrompter::ChangeEntry(Size new_idx)
@@ -9492,6 +9680,27 @@ void ConsolePrompter::Delete(Size start, Size end)
9492
9680
  }
9493
9681
  }
9494
9682
 
9683
+ void ConsolePrompter::FormatChoices(Span<const PromptChoice> choices, Size value)
9684
+ {
9685
+ int align = 0;
9686
+
9687
+ for (const PromptChoice &choice: choices) {
9688
+ align = std::max(align, (int)strlen(choice.str));
9689
+ }
9690
+
9691
+ str.RemoveFrom(0);
9692
+ str.Append('\n');
9693
+ for (Size i = 0; i < choices.len; i++) {
9694
+ const PromptChoice &choice = choices[i];
9695
+
9696
+ Fmt(&str, " [%1] %2 ", choice.c, FmtArg(choice.str).Pad(align));
9697
+ if (i == value) {
9698
+ str_offset = str.len;
9699
+ }
9700
+ str.Append('\n');
9701
+ }
9702
+ }
9703
+
9495
9704
  void ConsolePrompter::RenderRaw()
9496
9705
  {
9497
9706
  columns = GetConsoleSize().x;
@@ -9524,7 +9733,7 @@ void ConsolePrompter::RenderRaw()
9524
9733
  int width = mask ? mask_columns : ComputeWidth(str.Take(i, bytes));
9525
9734
 
9526
9735
  if (x2 + width >= columns || str[i] == '\n') {
9527
- FmtArg prefix = FmtArg(str[i] == '\n' ? '.' : ' ').Repeat(prompt_columns - 1);
9736
+ FmtArg prefix = FmtArg(' ').Repeat(prompt_columns - 1);
9528
9737
  Print(StdErr, "\x1B[0K\r\n%!D.+%1%!0 %!..+", prefix);
9529
9738
 
9530
9739
  x2 = prompt_columns;
@@ -9568,8 +9777,10 @@ void ConsolePrompter::RenderBuffered()
9568
9777
  Print(StdErr, "%1%2", prompt, line);
9569
9778
  while (remain.len) {
9570
9779
  line = SplitStr(remain, '\n', &remain);
9571
- Print(StdErr, "\n%1 %2", FmtArg('.').Repeat(prompt_columns - 1), line);
9780
+ Print(StdErr, "\n%1%2", FmtArg(' ').Repeat(prompt_columns), line);
9572
9781
  }
9782
+
9783
+ StdErr->Flush();
9573
9784
  }
9574
9785
 
9575
9786
  Vec2<int> ConsolePrompter::GetConsoleSize()
@@ -9732,12 +9943,18 @@ error:
9732
9943
 
9733
9944
  int ConsolePrompter::ComputeWidth(Span<const char> str)
9734
9945
  {
9946
+ Size i = 0;
9735
9947
  int width = 0;
9736
9948
 
9737
- for (char c: str) {
9738
- // XXX: For now we assume all codepoints take a single column,
9739
- // we need to use something like wcwidth() instead.
9740
- width += ((uint8_t)c >= 32 && !((c & 0xC0) == 0x80));
9949
+ while (i < str.len) {
9950
+ int32_t uc;
9951
+ Size bytes = DecodeUtf8(str, i, &uc);
9952
+
9953
+ if (!bytes) [[unlikely]]
9954
+ return false;
9955
+
9956
+ i += bytes;
9957
+ width += ComputeCharacterWidth(uc);
9741
9958
  }
9742
9959
 
9743
9960
  return width;
@@ -9769,12 +9986,42 @@ const char *Prompt(const char *prompt, const char *default_value, const char *ma
9769
9986
  return str;
9770
9987
  }
9771
9988
 
9772
- bool PromptYN(const char *prompt, bool *out_value)
9989
+ Size PromptEnum(const char *prompt, Span<const PromptChoice> choices, Size value)
9773
9990
  {
9991
+ #if defined(RG_DEBUG)
9992
+ {
9993
+ HashSet<char> keys;
9994
+
9995
+ for (const PromptChoice &choice: choices) {
9996
+ keys.Set(choice.c);
9997
+ }
9998
+
9999
+ bool duplicates = (keys.table.count < choices.len);
10000
+ RG_ASSERT(!duplicates);
10001
+ }
10002
+ #endif
10003
+
9774
10004
  ConsolePrompter prompter;
9775
10005
  prompter.prompt = prompt;
9776
10006
 
9777
- return prompter.ReadYN(out_value);
10007
+ return prompter.ReadEnum(choices, value);
10008
+ }
10009
+
10010
+ Size PromptEnum(const char *prompt, Span<const char *const> strings, Size value)
10011
+ {
10012
+ static const char literals[] = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
10013
+ RG_ASSERT(strings.len <= RG_LEN(literals));
10014
+
10015
+ HeapArray<PromptChoice> choices;
10016
+
10017
+ for (Size i = 0; i < strings.len; i++) {
10018
+ const char *str = strings[i];
10019
+ PromptChoice choice = { literals[i], str };
10020
+
10021
+ choices.Append(choice);
10022
+ }
10023
+
10024
+ return PromptEnum(prompt, choices, value);
9778
10025
  }
9779
10026
 
9780
10027
  // ------------------------------------------------------------------------
@@ -9856,4 +10103,194 @@ bool CanCompressFile(const char *filename)
9856
10103
  return true;
9857
10104
  }
9858
10105
 
10106
+ // ------------------------------------------------------------------------
10107
+ // Unicode
10108
+ // ------------------------------------------------------------------------
10109
+
10110
+ static bool TestUnicodeTable(Span<const int32_t> table, int32_t uc)
10111
+ {
10112
+ RG_ASSERT(table.len > 0);
10113
+ RG_ASSERT(table.len % 2 == 0);
10114
+
10115
+ auto it = std::upper_bound(table.begin(), table.end(), uc,
10116
+ [](int32_t uc, int32_t x) { return uc < x; });
10117
+ Size idx = it - table.ptr;
10118
+
10119
+ // Each pair of value in table represents a valid interval
10120
+ return idx & 0x1;
10121
+ }
10122
+
10123
+ int ComputeCharacterWidth(int32_t uc)
10124
+ {
10125
+ if (uc < 32)
10126
+ return 0;
10127
+
10128
+ if (TestUnicodeTable(WcWidthNull, uc))
10129
+ return 0;
10130
+ if (TestUnicodeTable(WcWidthWide, uc))
10131
+ return 2;
10132
+
10133
+ return 1;
10134
+ }
10135
+
10136
+ bool IsXidStart(int32_t uc)
10137
+ {
10138
+ if (IsAsciiAlpha(uc))
10139
+ return true;
10140
+ if (uc == '_')
10141
+ return true;
10142
+ if (TestUnicodeTable(XidStartTable, uc))
10143
+ return true;
10144
+
10145
+ return false;
10146
+ }
10147
+
10148
+ bool IsXidContinue(int32_t uc)
10149
+ {
10150
+ if (IsAsciiAlphaOrDigit(uc))
10151
+ return true;
10152
+ if (uc == '_')
10153
+ return true;
10154
+ if (TestUnicodeTable(XidContinueTable, uc))
10155
+ return true;
10156
+
10157
+ return false;
10158
+ }
10159
+
10160
+ // ------------------------------------------------------------------------
10161
+ // CRC
10162
+ // ------------------------------------------------------------------------
10163
+
10164
+ uint32_t CRC32(uint32_t state, Span<const uint8_t> buf)
10165
+ {
10166
+ state = ~state;
10167
+
10168
+ Size right = buf.len & (RG_SIZE_MAX - 3);
10169
+
10170
+ for (Size i = 0; i < right; i += 4) {
10171
+ state = (state >> 8) ^ Crc32Table[(state ^ buf[i + 0]) & 0xFF];
10172
+ state = (state >> 8) ^ Crc32Table[(state ^ buf[i + 1]) & 0xFF];
10173
+ state = (state >> 8) ^ Crc32Table[(state ^ buf[i + 2]) & 0xFF];
10174
+ state = (state >> 8) ^ Crc32Table[(state ^ buf[i + 3]) & 0xFF];
10175
+ }
10176
+ for (Size i = right; i < buf.len; i++) {
10177
+ state = (state >> 8) ^ Crc32Table[(state ^ buf[i]) & 0xFF];
10178
+ }
10179
+
10180
+ return ~state;
10181
+ }
10182
+
10183
+ uint32_t CRC32C(uint32_t state, Span<const uint8_t> buf)
10184
+ {
10185
+ state = ~state;
10186
+
10187
+ Size right = buf.len & (RG_SIZE_MAX - 3);
10188
+
10189
+ for (Size i = 0; i < right; i += 4) {
10190
+ state = (state >> 8) ^ Crc32CTable[(state ^ buf[i + 0]) & 0xFF];
10191
+ state = (state >> 8) ^ Crc32CTable[(state ^ buf[i + 1]) & 0xFF];
10192
+ state = (state >> 8) ^ Crc32CTable[(state ^ buf[i + 2]) & 0xFF];
10193
+ state = (state >> 8) ^ Crc32CTable[(state ^ buf[i + 3]) & 0xFF];
10194
+ }
10195
+ for (Size i = right; i < buf.len; i++) {
10196
+ state = (state >> 8) ^ Crc32CTable[(state ^ buf[i]) & 0xFF];
10197
+ }
10198
+
10199
+ return ~state;
10200
+ }
10201
+
10202
+ static uint64_t XzUpdate1(uint64_t state, uint8_t byte)
10203
+ {
10204
+ uint64_t ret = (state >> 8) ^ Crc64XzTable0[byte ^ (uint8_t)state];
10205
+ return ret;
10206
+ }
10207
+
10208
+ static uint64_t XzUpdate16(uint64_t state, const uint8_t *bytes)
10209
+ {
10210
+ uint64_t ret = Crc64XzTable0[bytes[15]] ^
10211
+ Crc64XzTable1[bytes[14]] ^
10212
+ Crc64XzTable2[bytes[13]] ^
10213
+ Crc64XzTable3[bytes[12]] ^
10214
+ Crc64XzTable4[bytes[11]] ^
10215
+ Crc64XzTable5[bytes[10]] ^
10216
+ Crc64XzTable6[bytes[9]] ^
10217
+ Crc64XzTable7[bytes[8]] ^
10218
+ Crc64XzTable8[bytes[7] ^ (uint8_t)(state >> 56)] ^
10219
+ Crc64XzTable9[bytes[6] ^ (uint8_t)(state >> 48)] ^
10220
+ Crc64XzTable10[bytes[5] ^ (uint8_t)(state >> 40)] ^
10221
+ Crc64XzTable11[bytes[4] ^ (uint8_t)(state >> 32)] ^
10222
+ Crc64XzTable12[bytes[3] ^ (uint8_t)(state >> 24)] ^
10223
+ Crc64XzTable13[bytes[2] ^ (uint8_t)(state >> 16)] ^
10224
+ Crc64XzTable14[bytes[1] ^ (uint8_t)(state >> 8)] ^
10225
+ Crc64XzTable15[bytes[0] ^ (uint8_t)(state >> 0)];
10226
+ return ret;
10227
+ }
10228
+
10229
+ uint64_t CRC64xz(uint64_t state, Span<const uint8_t> buf)
10230
+ {
10231
+ state = ~state;
10232
+
10233
+ Size left = std::min(buf.len, (Size)(AlignUp(buf.ptr, 16) - buf.ptr));
10234
+ Size right = std::max(left, (Size)(AlignDown(buf.end(), 16) - buf.ptr));
10235
+
10236
+ for (Size i = 0; i < left; i++) {
10237
+ state = XzUpdate1(state, buf[i]);
10238
+ }
10239
+ for (Size i = left; i < right; i += 16) {
10240
+ state = XzUpdate16(state, buf.ptr + i);
10241
+ }
10242
+ for (Size i = right; i < buf.len; i++) {
10243
+ state = XzUpdate1(state, buf[i]);
10244
+ }
10245
+
10246
+ return ~state;
10247
+ }
10248
+
10249
+ static uint64_t NvmeUpdate1(uint64_t state, uint8_t byte)
10250
+ {
10251
+ uint64_t ret = (state >> 8) ^ Crc64NvmeTable0[byte ^ (uint8_t)state];
10252
+ return ret;
10253
+ }
10254
+
10255
+ static uint64_t NvmeUpdate16(uint64_t state, const uint8_t *bytes)
10256
+ {
10257
+ uint64_t ret = Crc64NvmeTable0[bytes[15]] ^
10258
+ Crc64NvmeTable1[bytes[14]] ^
10259
+ Crc64NvmeTable2[bytes[13]] ^
10260
+ Crc64NvmeTable3[bytes[12]] ^
10261
+ Crc64NvmeTable4[bytes[11]] ^
10262
+ Crc64NvmeTable5[bytes[10]] ^
10263
+ Crc64NvmeTable6[bytes[9]] ^
10264
+ Crc64NvmeTable7[bytes[8]] ^
10265
+ Crc64NvmeTable8[bytes[7] ^ (uint8_t)(state >> 56)] ^
10266
+ Crc64NvmeTable9[bytes[6] ^ (uint8_t)(state >> 48)] ^
10267
+ Crc64NvmeTable10[bytes[5] ^ (uint8_t)(state >> 40)] ^
10268
+ Crc64NvmeTable11[bytes[4] ^ (uint8_t)(state >> 32)] ^
10269
+ Crc64NvmeTable12[bytes[3] ^ (uint8_t)(state >> 24)] ^
10270
+ Crc64NvmeTable13[bytes[2] ^ (uint8_t)(state >> 16)] ^
10271
+ Crc64NvmeTable14[bytes[1] ^ (uint8_t)(state >> 8)] ^
10272
+ Crc64NvmeTable15[bytes[0] ^ (uint8_t)(state >> 0)];
10273
+ return ret;
10274
+ }
10275
+
10276
+ uint64_t CRC64nvme(uint64_t state, Span<const uint8_t> buf)
10277
+ {
10278
+ state = ~state;
10279
+
10280
+ Size left = std::min(buf.len, (Size)(AlignUp(buf.ptr, 16) - buf.ptr));
10281
+ Size right = std::max(left, (Size)(AlignDown(buf.end(), 16) - buf.ptr));
10282
+
10283
+ for (Size i = 0; i < left; i++) {
10284
+ state = NvmeUpdate1(state, buf[i]);
10285
+ }
10286
+ for (Size i = left; i < right; i += 16) {
10287
+ state = NvmeUpdate16(state, buf.ptr + i);
10288
+ }
10289
+ for (Size i = right; i < buf.len; i++) {
10290
+ state = NvmeUpdate1(state, buf[i]);
10291
+ }
10292
+
10293
+ return ~state;
10294
+ }
10295
+
9859
10296
  }