koffi 2.7.1 → 2.7.2

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,12 @@
4
4
 
5
5
  ### Koffi 2.7
6
6
 
7
+ #### Koffi 2.7.2 (2024-01-15)
8
+
9
+ - Add missing TypeScript declaration for `koffi.free()`
10
+ - Fix TypeScript declaration of `koffi.encode()`
11
+ - Try to improve compatibility with webpack
12
+
7
13
  #### Koffi 2.7.1 (2024-01-02)
8
14
 
9
15
  - Support C-like `int[3]` syntax for [fixed array types](input.md#fixed-size-c-arrays)
package/README.md CHANGED
@@ -25,9 +25,9 @@ Go to the web site for more information: https://koffi.dev/
25
25
 
26
26
  # Project history
27
27
 
28
- You can consult the [changelog](https://koffi.dev/changes) on the official website.
28
+ You can consult the [changelog](https://koffi.dev/changelog) on the official website.
29
29
 
30
- Major version increments can include breaking API changes, use the [migration guide](https://koffi.dev/changes#migration-guide) for more information.
30
+ Major version increments can include breaking API changes, use the [migration guide](https://koffi.dev/changelog#migration-guide) for more information.
31
31
 
32
32
  # License
33
33
 
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/unions.md CHANGED
@@ -49,8 +49,8 @@ const U = koffi.union('U', { i: 'int', str: 'char *' });
49
49
 
50
50
  const DoSomething = lib.func('void DoSomething(const char *type, U u)');
51
51
 
52
- DoSomething('int', { .i = 42 });
53
- DoSomething('string', { .str = 'Hello!' });
52
+ DoSomething('int', { i: 42 });
53
+ DoSomething('string', { str: 'Hello!' });
54
54
  ```
55
55
 
56
56
  ### Win32 example
package/index.d.ts CHANGED
@@ -132,7 +132,6 @@ declare module 'koffi' {
132
132
  export function decode(value: any, offset: number, type: TypeSpec, len: number): any;
133
133
  export function address(value: any): bigint;
134
134
  export function call(value: any, type: TypeSpec, ...args: any[]): any;
135
- export function encode(ref: any, type: TypeSpec): void;
136
135
  export function encode(ref: any, type: TypeSpec, value: any): void;
137
136
  export function encode(ref: any, type: TypeSpec, value: any, len: number): void;
138
137
  export function encode(ref: any, offset: number, type: TypeSpec): void;
@@ -151,6 +150,8 @@ declare module 'koffi' {
151
150
  export function config(cfg: Record<string, unknown>): Record<string, unknown>;
152
151
  export function stats(): Record<string, unknown>;
153
152
 
153
+ export function free(value: any): void;
154
+
154
155
  export function errno(): number;
155
156
  export function errno(value: number): number;
156
157
 
package/index.js CHANGED
@@ -69,17 +69,17 @@ var require_tools = __commonJS({
69
69
  try {
70
70
  crypto.randomFillSync(buf);
71
71
  let suffix = buf.toString("hex").padStart(8, "0");
72
- let filename = `${prefix}.${suffix}`;
73
- let file = fs2.createWriteStream(filename, { flags: "wx", mode: 420 });
74
- return [filename, file];
72
+ let filename2 = `${prefix}.${suffix}`;
73
+ let file = fs2.createWriteStream(filename2, { flags: "wx", mode: 420 });
74
+ return [filename2, file];
75
75
  } catch (err) {
76
76
  if (err.code != "EEXIST")
77
77
  throw err;
78
78
  }
79
79
  }
80
80
  }
81
- function extract_targz(filename, dest_dir, strip = 0) {
82
- let reader = fs2.createReadStream(filename).pipe(zlib.createGunzip());
81
+ function extract_targz(filename2, dest_dir, strip = 0) {
82
+ let reader = fs2.createReadStream(filename2).pipe(zlib.createGunzip());
83
83
  return new Promise((resolve, reject) => {
84
84
  let header = null;
85
85
  let extended = {};
@@ -119,13 +119,13 @@ var require_tools = __commonJS({
119
119
  }
120
120
  data = data.subarray(0, header.size);
121
121
  if (header.type == "0" || header.type == "7") {
122
- let filename2 = dest_dir + "/" + header.filename;
123
- let dirname = path.dirname(filename2);
122
+ let filename3 = dest_dir + "/" + header.filename;
123
+ let dirname = path.dirname(filename3);
124
124
  fs2.mkdirSync(dirname, { recursive: true, mode: 493 });
125
- fs2.writeFileSync(filename2, data, { mode: header.mode });
125
+ fs2.writeFileSync(filename3, data, { mode: header.mode });
126
126
  } else if (header.type == "5") {
127
- let filename2 = dest_dir + "/" + header.filename;
128
- fs2.mkdirSync(filename2, { recursive: true, mode: header.mode });
127
+ let filename3 = dest_dir + "/" + header.filename;
128
+ fs2.mkdirSync(filename3, { recursive: true, mode: header.mode });
129
129
  } else if (header.type == "L") {
130
130
  extended.filename = data.toString("utf-8").replace(/\0/g, "");
131
131
  } else if (header.type == "x") {
@@ -233,10 +233,10 @@ var require_tools = __commonJS({
233
233
  }
234
234
  return arch2;
235
235
  }
236
- function read_file_header(filename, read) {
236
+ function read_file_header(filename2, read) {
237
237
  let fd = null;
238
238
  try {
239
- let fd2 = fs2.openSync(filename);
239
+ let fd2 = fs2.openSync(filename2);
240
240
  let buf = Buffer.allocUnsafe(read);
241
241
  let len = fs2.readSync(fd2, buf);
242
242
  return buf.subarray(0, len);
@@ -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.7.1",
382
- stable: "2.7.1",
381
+ version: "2.7.2",
382
+ stable: "2.7.2",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
@@ -527,24 +527,24 @@ try {
527
527
  } catch (err) {
528
528
  }
529
529
  if (native == null) {
530
+ let roots = [__dirname];
531
+ if (process.resourcesPath != null)
532
+ roots.push(process.resourcesPath);
530
533
  let names = [
531
534
  `/build/koffi/${process.platform}_${arch}/koffi.node`,
532
535
  `/koffi/${process.platform}_${arch}/koffi.node`,
533
536
  `/node_modules/koffi/build/koffi/${process.platform}_${arch}/koffi.node`
534
537
  ];
535
- for (let name of names) {
536
- if (fs.existsSync(__dirname + name)) {
537
- native = require(__dirname + name);
538
- break;
539
- }
540
- }
541
- if (native == null && process.resourcesPath != null) {
538
+ for (let root of roots) {
542
539
  for (let name of names) {
543
- if (fs.existsSync(process.resourcesPath + name)) {
544
- native = require(process.resourcesPath + name);
540
+ let filename = root + name;
541
+ if (fs.existsSync(filename)) {
542
+ native = eval("require")(filename);
545
543
  break;
546
544
  }
547
545
  }
546
+ if (native != null)
547
+ break;
548
548
  }
549
549
  }
550
550
  if (native == null)
package/indirect.js CHANGED
@@ -69,17 +69,17 @@ var require_tools = __commonJS({
69
69
  try {
70
70
  crypto.randomFillSync(buf);
71
71
  let suffix = buf.toString("hex").padStart(8, "0");
72
- let filename = `${prefix}.${suffix}`;
73
- let file = fs2.createWriteStream(filename, { flags: "wx", mode: 420 });
74
- return [filename, file];
72
+ let filename2 = `${prefix}.${suffix}`;
73
+ let file = fs2.createWriteStream(filename2, { flags: "wx", mode: 420 });
74
+ return [filename2, file];
75
75
  } catch (err) {
76
76
  if (err.code != "EEXIST")
77
77
  throw err;
78
78
  }
79
79
  }
80
80
  }
81
- function extract_targz(filename, dest_dir, strip = 0) {
82
- let reader = fs2.createReadStream(filename).pipe(zlib.createGunzip());
81
+ function extract_targz(filename2, dest_dir, strip = 0) {
82
+ let reader = fs2.createReadStream(filename2).pipe(zlib.createGunzip());
83
83
  return new Promise((resolve, reject) => {
84
84
  let header = null;
85
85
  let extended = {};
@@ -119,13 +119,13 @@ var require_tools = __commonJS({
119
119
  }
120
120
  data = data.subarray(0, header.size);
121
121
  if (header.type == "0" || header.type == "7") {
122
- let filename2 = dest_dir + "/" + header.filename;
123
- let dirname = path.dirname(filename2);
122
+ let filename3 = dest_dir + "/" + header.filename;
123
+ let dirname = path.dirname(filename3);
124
124
  fs2.mkdirSync(dirname, { recursive: true, mode: 493 });
125
- fs2.writeFileSync(filename2, data, { mode: header.mode });
125
+ fs2.writeFileSync(filename3, data, { mode: header.mode });
126
126
  } else if (header.type == "5") {
127
- let filename2 = dest_dir + "/" + header.filename;
128
- fs2.mkdirSync(filename2, { recursive: true, mode: header.mode });
127
+ let filename3 = dest_dir + "/" + header.filename;
128
+ fs2.mkdirSync(filename3, { recursive: true, mode: header.mode });
129
129
  } else if (header.type == "L") {
130
130
  extended.filename = data.toString("utf-8").replace(/\0/g, "");
131
131
  } else if (header.type == "x") {
@@ -233,10 +233,10 @@ var require_tools = __commonJS({
233
233
  }
234
234
  return arch2;
235
235
  }
236
- function read_file_header(filename, read) {
236
+ function read_file_header(filename2, read) {
237
237
  let fd = null;
238
238
  try {
239
- let fd2 = fs2.openSync(filename);
239
+ let fd2 = fs2.openSync(filename2);
240
240
  let buf = Buffer.allocUnsafe(read);
241
241
  let len = fs2.readSync(fd2, buf);
242
242
  return buf.subarray(0, len);
@@ -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.7.1",
382
- stable: "2.7.1",
381
+ version: "2.7.2",
382
+ stable: "2.7.2",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
@@ -447,24 +447,24 @@ var arch = determine_arch();
447
447
  var triplet = `${process.platform}_${arch}`;
448
448
  var native = null;
449
449
  {
450
+ let roots = [__dirname];
451
+ if (process.resourcesPath != null)
452
+ roots.push(process.resourcesPath);
450
453
  let names = [
451
454
  `/build/koffi/${process.platform}_${arch}/koffi.node`,
452
455
  `/koffi/${process.platform}_${arch}/koffi.node`,
453
456
  `/node_modules/koffi/build/koffi/${process.platform}_${arch}/koffi.node`
454
457
  ];
455
- for (let name of names) {
456
- if (fs.existsSync(__dirname + name)) {
457
- native = require(__dirname + name);
458
- break;
459
- }
460
- }
461
- if (native == null && process.resourcesPath != null) {
458
+ for (let root of roots) {
462
459
  for (let name of names) {
463
- if (fs.existsSync(process.resourcesPath + name)) {
464
- native = require(process.resourcesPath + name);
460
+ let filename = root + name;
461
+ if (fs.existsSync(filename)) {
462
+ native = eval("require")(filename);
465
463
  break;
466
464
  }
467
465
  }
466
+ if (native != null)
467
+ break;
468
468
  }
469
469
  }
470
470
  if (native == null)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.7.1",
4
- "stable": "2.7.1",
3
+ "version": "2.7.2",
4
+ "stable": "2.7.2",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -26,7 +26,7 @@
26
26
 
27
27
  namespace RG {
28
28
 
29
- class BrotliDecompressor: public StreamDecompressor {
29
+ class BrotliDecompressor: public StreamDecoder {
30
30
  BrotliDecoderState *state = nullptr;
31
31
  bool done = false;
32
32
 
@@ -37,27 +37,23 @@ class BrotliDecompressor: public StreamDecompressor {
37
37
  Size out_len = 0;
38
38
 
39
39
  public:
40
- BrotliDecompressor(StreamReader *reader) : StreamDecompressor(reader) {}
40
+ BrotliDecompressor(StreamReader *reader, CompressionType type);
41
41
  ~BrotliDecompressor();
42
42
 
43
- bool Init(CompressionType type) override;
44
43
  Size Read(Size max_len, void *out_buf) override;
45
44
  };
46
45
 
47
- BrotliDecompressor::~BrotliDecompressor()
48
- {
49
- if (state) {
50
- BrotliDecoderDestroyInstance(state);
51
- }
52
- }
53
-
54
- bool BrotliDecompressor::Init(CompressionType)
46
+ BrotliDecompressor::BrotliDecompressor(StreamReader *reader, CompressionType)
47
+ : StreamDecoder(reader)
55
48
  {
56
49
  state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
57
50
  if (!state)
58
51
  throw std::bad_alloc();
52
+ }
59
53
 
60
- return true;
54
+ BrotliDecompressor::~BrotliDecompressor()
55
+ {
56
+ BrotliDecoderDestroyInstance(state);
61
57
  }
62
58
 
63
59
  Size BrotliDecompressor::Read(Size max_len, void *user_buf)
@@ -105,26 +101,19 @@ Size BrotliDecompressor::Read(Size max_len, void *user_buf)
105
101
  RG_UNREACHABLE();
106
102
  }
107
103
 
108
- class BrotliCompressor: public StreamCompressor {
104
+ class BrotliCompressor: public StreamEncoder {
109
105
  BrotliEncoderStateStruct *state = nullptr;
110
106
 
111
107
  public:
112
- BrotliCompressor(StreamWriter *writer) : StreamCompressor(writer) {}
108
+ BrotliCompressor(StreamWriter *writer, CompressionType type, CompressionSpeed speed);
113
109
  ~BrotliCompressor();
114
110
 
115
- bool Init(CompressionType type, CompressionSpeed speed) override;
116
111
  bool Write(Span<const uint8_t> buf) override;
117
112
  bool Finalize() override;
118
113
  };
119
114
 
120
- BrotliCompressor::~BrotliCompressor()
121
- {
122
- if (state) {
123
- BrotliEncoderDestroyInstance(state);
124
- }
125
- }
126
-
127
- bool BrotliCompressor::Init(CompressionType, CompressionSpeed speed)
115
+ BrotliCompressor::BrotliCompressor(StreamWriter *writer, CompressionType, CompressionSpeed speed)
116
+ : StreamEncoder(writer)
128
117
  {
129
118
  state = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
130
119
  if (!state)
@@ -137,8 +126,11 @@ bool BrotliCompressor::Init(CompressionType, CompressionSpeed speed)
137
126
  case CompressionSpeed::Slow: { BrotliEncoderSetParameter(state, BROTLI_PARAM_QUALITY, 11); } break;
138
127
  case CompressionSpeed::Fast: { BrotliEncoderSetParameter(state, BROTLI_PARAM_QUALITY, 0); } break;
139
128
  }
129
+ }
140
130
 
141
- return true;
131
+ BrotliCompressor::~BrotliCompressor()
132
+ {
133
+ BrotliEncoderDestroyInstance(state);
142
134
  }
143
135
 
144
136
  bool BrotliCompressor::Write(Span<const uint8_t> buf)
@@ -1932,6 +1932,23 @@ bool GetDebugFlag(const char *name)
1932
1932
  }
1933
1933
  }
1934
1934
 
1935
+ #ifndef NDEBUG
1936
+ const char *DebugLogContext(const char *filename, int line)
1937
+ {
1938
+ static RG_THREAD_LOCAL LocalArray<char, 1024> buf;
1939
+
1940
+ buf.len = Fmt(buf.data, " [%1:%2] ", filename, line).len;
1941
+
1942
+ if (buf.len > 32) {
1943
+ char *ptr = buf.end() - 32;
1944
+ memcpy(ptr, " [...", 6);
1945
+ return ptr;
1946
+ } else {
1947
+ return buf.data;
1948
+ }
1949
+ }
1950
+ #endif
1951
+
1935
1952
  static void RunLogFilter(Size idx, LogLevel level, const char *ctx, const char *msg)
1936
1953
  {
1937
1954
  const std::function<LogFilterFunc> &func = *log_filters[idx];
@@ -2001,9 +2018,9 @@ void DefaultLogHandler(LogLevel level, const char *ctx, const char *msg)
2001
2018
  {
2002
2019
  switch (level) {
2003
2020
  case LogLevel::Debug:
2004
- case LogLevel::Info: { PrintLn(stderr, "%!D..%1%2%!0%3", ctx ? ctx : "", ctx ? ": " : "", msg); } break;
2005
- case LogLevel::Warning: { PrintLn(stderr, "%!M..%1%2%!0%3", ctx ? ctx : "", ctx ? ": " : "", msg); } break;
2006
- case LogLevel::Error: { PrintLn(stderr, "%!R..%1%2%!0%3", ctx ? ctx : "", ctx ? ": " : "", msg); } break;
2021
+ case LogLevel::Info: { PrintLn(stderr, "%!D..%1%!0%2", ctx ? ctx : "", msg); } break;
2022
+ case LogLevel::Warning: { PrintLn(stderr, "%!M..%1%!0%2", ctx ? ctx : "", msg); } break;
2023
+ case LogLevel::Error: { PrintLn(stderr, "%!R..%1%!0%2", ctx ? ctx : "", msg); } break;
2007
2024
  }
2008
2025
 
2009
2026
  fflush(stderr);
@@ -2047,11 +2064,10 @@ bool RedirectLogToWindowsEvents(const char *name)
2047
2064
 
2048
2065
  // Append context
2049
2066
  if (ctx) {
2050
- Size len = ConvertUtf8ToWin32Wide(ctx, buf_w.Take(0, RG_LEN(buf_w.data) / 2));
2067
+ Size len = ConvertUtf8ToWin32Wide(ctx, buf_w.TakeAvailable());
2051
2068
  if (len < 0)
2052
2069
  return;
2053
- wcscpy(buf_w.data + len, L": ");
2054
- buf_w.len += len + 2;
2070
+ buf_w.len += len;
2055
2071
  }
2056
2072
 
2057
2073
  // Append message
@@ -6182,6 +6198,15 @@ StreamWriter stderr_st(stderr, "<stderr>");
6182
6198
  static CreateDecompressorFunc *DecompressorFunctions[RG_LEN(CompressionTypeNames)];
6183
6199
  static CreateCompressorFunc *CompressorFunctions[RG_LEN(CompressionTypeNames)];
6184
6200
 
6201
+ void StreamReader::SetDecoder(StreamDecoder *decoder)
6202
+ {
6203
+ RG_ASSERT(decoder);
6204
+ RG_ASSERT(!filename);
6205
+ RG_ASSERT(!this->decoder);
6206
+
6207
+ this->decoder = decoder;
6208
+ }
6209
+
6185
6210
  bool StreamReader::Open(Span<const uint8_t> buf, const char *filename,
6186
6211
  CompressionType compression_type)
6187
6212
  {
@@ -6286,9 +6311,9 @@ bool StreamReader::Close(bool implicit)
6286
6311
  {
6287
6312
  RG_ASSERT(implicit || this != &stdin_st);
6288
6313
 
6289
- if (decompressor) {
6290
- RG_ASSERT(compression_type != CompressionType::None);
6291
- delete decompressor;
6314
+ if (decoder) {
6315
+ delete decoder;
6316
+ decoder = nullptr;
6292
6317
  }
6293
6318
 
6294
6319
  switch (source.type) {
@@ -6308,7 +6333,6 @@ bool StreamReader::Close(bool implicit)
6308
6333
 
6309
6334
  filename = nullptr;
6310
6335
  error = true;
6311
- compression_type = CompressionType::None;
6312
6336
  source.type = SourceType::Memory;
6313
6337
  source.eof = false;
6314
6338
  eof = false;
@@ -6323,6 +6347,11 @@ bool StreamReader::Rewind()
6323
6347
  if (error) [[unlikely]]
6324
6348
  return false;
6325
6349
 
6350
+ if (decoder) [[unlikely]] {
6351
+ LogError("Cannot rewind stream with decoder");
6352
+ return false;
6353
+ }
6354
+
6326
6355
  switch (source.type) {
6327
6356
  case SourceType::Memory: { source.u.memory.pos = 0; } break;
6328
6357
  case SourceType::File: {
@@ -6339,14 +6368,6 @@ bool StreamReader::Rewind()
6339
6368
  } break;
6340
6369
  }
6341
6370
 
6342
- if (decompressor) {
6343
- RG_ASSERT(compression_type != CompressionType::None);
6344
- delete decompressor;
6345
-
6346
- if (!InitDecompressor(compression_type))
6347
- return false;
6348
- }
6349
-
6350
6371
  source.eof = false;
6351
6372
  eof = false;
6352
6373
 
@@ -6376,14 +6397,10 @@ Size StreamReader::Read(Span<uint8_t> out_buf)
6376
6397
  return -1;
6377
6398
 
6378
6399
  Size read_len = 0;
6379
- if (decompressor) {
6380
- RG_ASSERT(compression_type != CompressionType::None);
6381
-
6382
- read_len = decompressor->Read(out_buf.len, out_buf.ptr);
6400
+ if (decoder) {
6401
+ read_len = decoder->Read(out_buf.len, out_buf.ptr);
6383
6402
  error |= (read_len < 0);
6384
6403
  } else {
6385
- RG_ASSERT(compression_type == CompressionType::None);
6386
-
6387
6404
  read_len = ReadRaw(out_buf.len, out_buf.ptr);
6388
6405
  eof = source.eof;
6389
6406
  }
@@ -6421,7 +6438,7 @@ Size StreamReader::ReadAll(Size max_len, HeapArray<uint8_t> *out_buf)
6421
6438
  // For some files (such as in /proc), the file size is reported as 0 even though there
6422
6439
  // is content inside, because these files are generated on demand. So we need to take
6423
6440
  // the slow path for apparently empty files.
6424
- if (compression_type == CompressionType::None && ComputeRawLen() > 0) {
6441
+ if (!decoder && ComputeRawLen() > 0) {
6425
6442
  if (raw_len > max_len) {
6426
6443
  LogError("File '%1' is too large (limit = %2)", filename, FmtDiskSize(max_len));
6427
6444
  return -1;
@@ -6510,19 +6527,10 @@ bool StreamReader::InitDecompressor(CompressionType type)
6510
6527
  return false;
6511
6528
  }
6512
6529
 
6513
- decompressor = func(this);
6514
-
6515
- if (!decompressor) {
6516
- error = true;
6517
- return false;
6518
- }
6519
- if (!decompressor->Init(type)) {
6520
- error = true;
6521
- return false;
6522
- }
6530
+ decoder = func(this, type);
6531
+ RG_ASSERT(decoder);
6523
6532
  }
6524
6533
 
6525
- compression_type = type;
6526
6534
  return true;
6527
6535
  }
6528
6536
 
@@ -6621,23 +6629,23 @@ void LineReader::PushLogFilter()
6621
6629
  char ctx_buf[1024];
6622
6630
 
6623
6631
  if (line_number > 0) {
6624
- Fmt(ctx_buf, "%1(%2)%3%4", st->GetFileName(), line_number, ctx ? ": " : "", ctx ? ctx : "");
6632
+ Fmt(ctx_buf, "%1%2(%3): ", ctx ? ctx : "", st->GetFileName(), line_number);
6625
6633
  } else {
6626
- Fmt(ctx_buf, "%1%2%3", st->GetFileName(), ctx ? ": " : "", ctx ? ctx : "");
6634
+ Fmt(ctx_buf, "%1%2: ", ctx ? ctx : "", st->GetFileName());
6627
6635
  }
6628
6636
 
6629
6637
  func(level, ctx_buf, msg);
6630
6638
  });
6631
6639
  }
6632
6640
 
6633
- #ifdef LZ4_VERSION_MAJOR
6634
- struct LZ4CompressContext {
6635
- LZ4F_cctx *encoder;
6636
- LZ4F_preferences_t prefs = {};
6641
+ void StreamWriter::SetEncoder(StreamEncoder *encoder)
6642
+ {
6643
+ RG_ASSERT(encoder);
6644
+ RG_ASSERT(!filename);
6645
+ RG_ASSERT(!this->encoder);
6637
6646
 
6638
- HeapArray<uint8_t> buf;
6639
- };
6640
- #endif
6647
+ this->encoder = encoder;
6648
+ }
6641
6649
 
6642
6650
  bool StreamWriter::Open(HeapArray<uint8_t> *mem, const char *filename,
6643
6651
  CompressionType compression_type, CompressionSpeed compression_speed)
@@ -6802,10 +6810,8 @@ bool StreamWriter::Write(Span<const uint8_t> buf)
6802
6810
  if (error) [[unlikely]]
6803
6811
  return false;
6804
6812
 
6805
- if (compressor) {
6806
- RG_ASSERT(compression_type != CompressionType::None);
6807
-
6808
- error |= !compressor->Write(buf);
6813
+ if (encoder) {
6814
+ error |= !encoder->Write(buf);
6809
6815
  return !error;
6810
6816
  } else {
6811
6817
  return WriteRaw(buf);
@@ -6817,11 +6823,11 @@ bool StreamWriter::Close(bool implicit)
6817
6823
  RG_ASSERT(implicit || this != &stdout_st);
6818
6824
  RG_ASSERT(implicit || this != &stderr_st);
6819
6825
 
6820
- if (compressor && !error) {
6821
- RG_ASSERT(compression_type != CompressionType::None);
6822
- error |= !compressor->Finalize();
6826
+ if (encoder) {
6827
+ error = error || !encoder->Finalize();
6823
6828
 
6824
- delete compressor;
6829
+ delete encoder;
6830
+ encoder = nullptr;
6825
6831
  }
6826
6832
 
6827
6833
  switch (dest.type) {
@@ -6880,7 +6886,6 @@ bool StreamWriter::Close(bool implicit)
6880
6886
 
6881
6887
  filename = nullptr;
6882
6888
  error = true;
6883
- compression_type = CompressionType::None;
6884
6889
  dest.type = DestinationType::Memory;
6885
6890
  str_alloc.ReleaseAll();
6886
6891
 
@@ -6898,21 +6903,10 @@ bool StreamWriter::InitCompressor(CompressionType type, CompressionSpeed speed)
6898
6903
  return false;
6899
6904
  }
6900
6905
 
6901
- compressor = func(this);
6902
-
6903
- if (!compressor) {
6904
- error = true;
6905
- return false;
6906
- }
6907
- if (!compressor->Init(type, speed)) {
6908
- error = true;
6909
- return false;
6910
- }
6906
+ encoder = func(this, type, speed);
6907
+ RG_ASSERT(encoder);
6911
6908
  }
6912
6909
 
6913
- compression_type = type;
6914
- compression_speed = speed;
6915
-
6916
6910
  return true;
6917
6911
  }
6918
6912
 
@@ -7013,14 +7007,14 @@ bool IsDecompressorAvailable(CompressionType compression_type)
7013
7007
  static bool CheckIniKey(Span<const char> key)
7014
7008
  {
7015
7009
  const auto test_char = [](char c) { return IsAsciiAlphaOrDigit(c) || c == '_' ||
7016
- c == '-' || c == '.' || c == '/'; };
7010
+ c == '-' || c == '.' || c == '/' || c == '@'; };
7017
7011
 
7018
7012
  if (!key.len) {
7019
7013
  LogError("INI key cannot be empty");
7020
7014
  return false;
7021
7015
  }
7022
7016
  if (!std::all_of(key.begin(), key.end(), test_char)) {
7023
- LogError("INI key must only contain alphanumeric, '.', '-' or '_' characters");
7017
+ LogError("INI key must only contain alphanumeric, '.', '-', '_', '/' or '@' characters");
7024
7018
  return false;
7025
7019
  }
7026
7020
 
@@ -249,14 +249,6 @@ extern "C" void AssertMessage(const char *filename, int line, const char *cond);
249
249
  #define RG_UNREACHABLE() __assume(0)
250
250
  #endif
251
251
 
252
- #if defined(__EMSCRIPTEN__)
253
- #define RG_EXPORT EMSCRIPTEN_KEEPALIVE
254
- #elif defined(_WIN32)
255
- #define RG_EXPORT __declspec(dllexport)
256
- #else
257
- #define RG_EXPORT __attribute__((visibility("default")))
258
- #endif
259
-
260
252
  #define RG_DELETE_COPY(Cls) \
261
253
  Cls(const Cls&) = delete; \
262
254
  Cls &operator=(const Cls&) = delete;
@@ -1221,11 +1213,7 @@ public:
1221
1213
  RG_ASSERT(len <= N - count);
1222
1214
 
1223
1215
  T *first = data + len;
1224
- #if __cplusplus >= 201703L
1225
1216
  if constexpr(!std::is_trivial<T>::value) {
1226
- #else
1227
- if (true) {
1228
- #endif
1229
1217
  for (Size i = 0; i < count; i++) {
1230
1218
  new (data + len) T();
1231
1219
  len++;
@@ -1323,11 +1311,7 @@ public:
1323
1311
  {
1324
1312
  RemoveFrom(0);
1325
1313
  Grow(other.capacity);
1326
- #if __cplusplus >= 201703L
1327
1314
  if constexpr(!std::is_trivial<T>::value) {
1328
- #else
1329
- if (true) {
1330
- #endif
1331
1315
  for (Size i = 0; i < other.len; i++) {
1332
1316
  ptr[i] = other.ptr[i];
1333
1317
  }
@@ -1432,11 +1416,7 @@ public:
1432
1416
  Grow(count);
1433
1417
 
1434
1418
  T *first = ptr + len;
1435
- #if __cplusplus >= 201703L
1436
1419
  if constexpr(!std::is_trivial<T>::value) {
1437
- #else
1438
- if (true) {
1439
- #endif
1440
1420
  for (Size i = 0; i < count; i++) {
1441
1421
  new (ptr + len) T();
1442
1422
  len++;
@@ -1454,11 +1434,7 @@ public:
1454
1434
  Grow();
1455
1435
 
1456
1436
  T *first = ptr + len;
1457
- #if __cplusplus >= 201703L
1458
1437
  if constexpr(!std::is_trivial<T>::value) {
1459
- #else
1460
- if (true) {
1461
- #endif
1462
1438
  new (ptr + len) T;
1463
1439
  }
1464
1440
  ptr[len++] = value;
@@ -1470,11 +1446,7 @@ public:
1470
1446
 
1471
1447
  T *first = ptr + len;
1472
1448
  for (const T &value: values) {
1473
- #if __cplusplus >= 201703L
1474
1449
  if constexpr(!std::is_trivial<T>::value) {
1475
- #else
1476
- if (true) {
1477
- #endif
1478
1450
  new (ptr + len) T;
1479
1451
  }
1480
1452
  ptr[len++] = value;
@@ -1486,11 +1458,7 @@ public:
1486
1458
  {
1487
1459
  RG_ASSERT(first >= 0 && first <= len);
1488
1460
 
1489
- #if __cplusplus >= 201703L
1490
1461
  if constexpr(!std::is_trivial<T>::value) {
1491
- #else
1492
- if (true) {
1493
- #endif
1494
1462
  for (Size i = first; i < len; i++) {
1495
1463
  ptr[i].~T();
1496
1464
  }
@@ -1796,11 +1764,7 @@ private:
1796
1764
  void DeleteValues([[maybe_unused]] iterator_type begin,
1797
1765
  [[maybe_unused]] iterator_type end)
1798
1766
  {
1799
- #if __cplusplus >= 201703L
1800
1767
  if constexpr(!std::is_trivial<T>::value) {
1801
- #else
1802
- if (true) {
1803
- #endif
1804
1768
  for (iterator_type it = begin; it != end; ++it) {
1805
1769
  it->~T();
1806
1770
  }
@@ -2077,11 +2041,7 @@ public:
2077
2041
  }
2078
2042
  ~HashTable()
2079
2043
  {
2080
- #if __cplusplus >= 201703L
2081
2044
  if constexpr(std::is_trivial<ValueType>::value) {
2082
- #else
2083
- if (false) {
2084
- #endif
2085
2045
  count = 0;
2086
2046
  Rehash(0);
2087
2047
  } else {
@@ -2278,11 +2238,7 @@ private:
2278
2238
  template <typename T = KeyType>
2279
2239
  const ValueType *Find(Size *idx, const T &key) const
2280
2240
  {
2281
- #if __cplusplus >= 201703L
2282
2241
  if constexpr(std::is_pointer<ValueType>::value) {
2283
- #else
2284
- if (false) {
2285
- #endif
2286
2242
  while (data[*idx]) {
2287
2243
  const KeyType &it_key = Handler::GetKey(data[*idx]);
2288
2244
  if (Handler::TestKeys(it_key, key))
@@ -3184,18 +3140,22 @@ static inline void Log(LogLevel level, const char *ctx, const char *fmt, Args...
3184
3140
 
3185
3141
  // Shortcut log functions
3186
3142
  #ifdef RG_DEBUG
3187
- template <typename... Args>
3188
- static inline void LogDebug(Args... args) { Log(LogLevel::Debug, "Debug", args...); }
3143
+ const char *DebugLogContext(const char *filename, int line);
3144
+
3145
+ #define LogDebug(...) Log(LogLevel::Debug, DebugLogContext(__FILE__, __LINE__) __VA_OPT__(,) __VA_ARGS__)
3146
+ #define LogInfo(...) Log(LogLevel::Info, nullptr __VA_OPT__(,) __VA_ARGS__)
3147
+ #define LogWarning(...) Log(LogLevel::Warning, DebugLogContext(__FILE__, __LINE__) __VA_OPT__(,) __VA_ARGS__)
3148
+ #define LogError(...) Log(LogLevel::Error, DebugLogContext(__FILE__, __LINE__) __VA_OPT__(,) __VA_ARGS__)
3189
3149
  #else
3190
- template <typename... Args>
3191
- static inline void LogDebug(Args...) {}
3150
+ template <typename... Args>
3151
+ static inline void LogDebug(Args...) {}
3152
+ template <typename... Args>
3153
+ static inline void LogInfo(Args... args) { Log(LogLevel::Info, nullptr, args...); }
3154
+ template <typename... Args>
3155
+ static inline void LogWarning(Args... args) { Log(LogLevel::Warning, "Warning: ", args...); }
3156
+ template <typename... Args>
3157
+ static inline void LogError(Args... args) { Log(LogLevel::Error, "Error: ", args...); }
3192
3158
  #endif
3193
- template <typename... Args>
3194
- static inline void LogInfo(Args... args) { Log(LogLevel::Info, nullptr, args...); }
3195
- template <typename... Args>
3196
- static inline void LogWarning(Args... args) { Log(LogLevel::Warning, "Warning", args...); }
3197
- template <typename... Args>
3198
- static inline void LogError(Args... args) { Log(LogLevel::Error, "Error", args...); }
3199
3159
 
3200
3160
  void SetLogHandler(const std::function<LogFunc> &func);
3201
3161
  void DefaultLogHandler(LogLevel level, const char *ctx, const char *msg);
@@ -3301,7 +3261,7 @@ static inline bool TestStrI(Span<const char> str1, const char *str2)
3301
3261
  return (i == str1.len) && !str2[i];
3302
3262
  }
3303
3263
  static inline bool TestStrI(const char *str1, Span<const char> str2)
3304
- { return TestStr(str2, str1); }
3264
+ { return TestStrI(str2, str1); }
3305
3265
  static inline bool TestStrI(const char *str1, const char *str2)
3306
3266
  {
3307
3267
  Size i = 0;
@@ -4344,8 +4304,8 @@ enum class CompressionSpeed {
4344
4304
  Fast
4345
4305
  };
4346
4306
 
4347
- class StreamDecompressor;
4348
- class StreamCompressor;
4307
+ class StreamDecoder;
4308
+ class StreamEncoder;
4349
4309
 
4350
4310
  class StreamReader {
4351
4311
  RG_DELETE_COPY(StreamReader)
@@ -4383,8 +4343,7 @@ class StreamReader {
4383
4343
  bool eof = false;
4384
4344
  } source;
4385
4345
 
4386
- CompressionType compression_type = CompressionType::None;
4387
- StreamDecompressor *decompressor = nullptr;
4346
+ StreamDecoder *decoder = nullptr;
4388
4347
 
4389
4348
  int64_t raw_len = -1;
4390
4349
  Size raw_read = 0;
@@ -4408,6 +4367,9 @@ public:
4408
4367
  : StreamReader() { Open(func, filename, compression_type); }
4409
4368
  ~StreamReader() { Close(true); }
4410
4369
 
4370
+ // Call before Open!
4371
+ void SetDecoder(StreamDecoder *decoder);
4372
+
4411
4373
  bool Open(Span<const uint8_t> buf, const char *filename = nullptr,
4412
4374
  CompressionType compression_type = CompressionType::None);
4413
4375
  bool Open(FILE *fp, const char *filename,
@@ -4421,7 +4383,6 @@ public:
4421
4383
  bool Rewind();
4422
4384
 
4423
4385
  const char *GetFileName() const { return filename; }
4424
- CompressionType GetCompressionType() const { return compression_type; }
4425
4386
  int64_t GetReadLimit() { return read_max; }
4426
4387
  bool IsValid() const { return filename && !error; }
4427
4388
  bool IsEOF() const { return eof; }
@@ -4449,62 +4410,38 @@ private:
4449
4410
 
4450
4411
  Size ReadRaw(Size max_len, void *out_buf);
4451
4412
 
4452
- friend class StreamDecompressor;
4413
+ friend class StreamDecoder;
4453
4414
  };
4454
4415
 
4455
- static inline Size ReadFile(const char *filename, CompressionType compression_type, Span<uint8_t> out_buf)
4456
- {
4457
- StreamReader st(filename, compression_type);
4458
- return st.Read(out_buf);
4459
- }
4460
4416
  static inline Size ReadFile(const char *filename, Span<uint8_t> out_buf)
4461
4417
  {
4462
4418
  StreamReader st(filename);
4463
4419
  return st.Read(out_buf);
4464
4420
  }
4465
- static inline Size ReadFile(const char *filename, CompressionType compression_type, Span<char> out_buf)
4466
- {
4467
- StreamReader st(filename, compression_type);
4468
- return st.Read(out_buf);
4469
- }
4470
4421
  static inline Size ReadFile(const char *filename, Span<char> out_buf)
4471
4422
  {
4472
4423
  StreamReader st(filename);
4473
4424
  return st.Read(out_buf);
4474
4425
  }
4475
-
4476
- static inline Size ReadFile(const char *filename, Size max_len, CompressionType compression_type,
4477
- HeapArray<uint8_t> *out_buf)
4478
- {
4479
- StreamReader st(filename, compression_type);
4480
- return st.ReadAll(max_len, out_buf);
4481
- }
4482
4426
  static inline Size ReadFile(const char *filename, Size max_len, HeapArray<uint8_t> *out_buf)
4483
4427
  {
4484
4428
  StreamReader st(filename);
4485
4429
  return st.ReadAll(max_len, out_buf);
4486
4430
  }
4487
- static inline Size ReadFile(const char *filename, Size max_len, CompressionType compression_type,
4488
- HeapArray<char> *out_buf)
4489
- {
4490
- StreamReader st(filename, compression_type);
4491
- return st.ReadAll(max_len, out_buf);
4492
- }
4493
4431
  static inline Size ReadFile(const char *filename, Size max_len, HeapArray<char> *out_buf)
4494
4432
  {
4495
4433
  StreamReader st(filename);
4496
4434
  return st.ReadAll(max_len, out_buf);
4497
4435
  }
4498
4436
 
4499
- class StreamDecompressor {
4437
+ class StreamDecoder {
4500
4438
  protected:
4501
4439
  StreamReader *reader;
4502
4440
 
4503
4441
  public:
4504
- StreamDecompressor(StreamReader *reader) : reader(reader) {}
4505
- virtual ~StreamDecompressor() {}
4442
+ StreamDecoder(StreamReader *reader) : reader(reader) {}
4443
+ virtual ~StreamDecoder() {}
4506
4444
 
4507
- virtual bool Init(CompressionType type) = 0;
4508
4445
  virtual Size Read(Size max_len, void *out_buf) = 0;
4509
4446
 
4510
4447
  protected:
@@ -4517,7 +4454,7 @@ protected:
4517
4454
  void SetEOF(bool eof) { reader->eof = eof; }
4518
4455
  };
4519
4456
 
4520
- typedef StreamDecompressor *CreateDecompressorFunc(StreamReader *reader);
4457
+ typedef StreamDecoder *CreateDecompressorFunc(StreamReader *reader, CompressionType type);
4521
4458
 
4522
4459
  class StreamDecompressorHelper {
4523
4460
  public:
@@ -4525,9 +4462,9 @@ public:
4525
4462
  };
4526
4463
 
4527
4464
  #define RG_REGISTER_DECOMPRESSOR(Type, Cls) \
4528
- static StreamDecompressor *RG_UNIQUE_NAME(CreateDecompressor)(StreamReader *reader) \
4465
+ static StreamDecoder *RG_UNIQUE_NAME(CreateDecompressor)(StreamReader *reader, CompressionType type) \
4529
4466
  { \
4530
- StreamDecompressor *decompressor = new Cls(reader); \
4467
+ StreamDecoder *decompressor = new Cls(reader, type); \
4531
4468
  return decompressor; \
4532
4469
  } \
4533
4470
  static StreamDecompressorHelper RG_UNIQUE_NAME(CreateDecompressorHelper)((Type), RG_UNIQUE_NAME(CreateDecompressor))
@@ -4601,9 +4538,7 @@ class StreamWriter {
4601
4538
  bool vt100;
4602
4539
  } dest;
4603
4540
 
4604
- CompressionType compression_type = CompressionType::None;
4605
- CompressionSpeed compression_speed = CompressionSpeed::Default;
4606
- StreamCompressor *compressor = nullptr;
4541
+ StreamEncoder *encoder = nullptr;
4607
4542
 
4608
4543
  int64_t raw_written = 0;
4609
4544
 
@@ -4633,6 +4568,9 @@ public:
4633
4568
  : StreamWriter() { Open(func, filename, compression_type, compression_speed); }
4634
4569
  ~StreamWriter() { Close(true); }
4635
4570
 
4571
+ // Call before Open!
4572
+ void SetEncoder(StreamEncoder *encoder);
4573
+
4636
4574
  bool Open(HeapArray<uint8_t> *mem, const char *filename = nullptr,
4637
4575
  CompressionType compression_type = CompressionType::None,
4638
4576
  CompressionSpeed compression_speed = CompressionSpeed::Default);
@@ -4655,8 +4593,6 @@ public:
4655
4593
  bool Flush();
4656
4594
 
4657
4595
  const char *GetFileName() const { return filename; }
4658
- CompressionType GetCompressionType() const { return compression_type; }
4659
- CompressionSpeed GetCompressionSpeed() const { return compression_speed; }
4660
4596
  bool IsVt100() const { return dest.vt100; }
4661
4597
  bool IsValid() const { return filename && !error; }
4662
4598
 
@@ -4677,33 +4613,30 @@ private:
4677
4613
 
4678
4614
  bool WriteRaw(Span<const uint8_t> buf);
4679
4615
 
4680
- friend class StreamCompressor;
4616
+ friend class StreamEncoder;
4681
4617
  };
4682
4618
 
4683
- static inline bool WriteFile(Span<const uint8_t> buf, const char *filename, unsigned int flags = 0,
4684
- CompressionType compression_type = CompressionType::None)
4619
+ static inline bool WriteFile(Span<const uint8_t> buf, const char *filename, unsigned int flags = 0)
4685
4620
  {
4686
- StreamWriter st(filename, flags, compression_type);
4621
+ StreamWriter st(filename, flags);
4687
4622
  st.Write(buf);
4688
4623
  return st.Close();
4689
4624
  }
4690
- static inline bool WriteFile(Span<const char> buf, const char *filename, unsigned int flags = 0,
4691
- CompressionType compression_type = CompressionType::None)
4625
+ static inline bool WriteFile(Span<const char> buf, const char *filename, unsigned int flags = 0)
4692
4626
  {
4693
- StreamWriter st(filename, flags, compression_type);
4627
+ StreamWriter st(filename, flags);
4694
4628
  st.Write(buf);
4695
4629
  return st.Close();
4696
4630
  }
4697
4631
 
4698
- class StreamCompressor {
4632
+ class StreamEncoder {
4699
4633
  protected:
4700
4634
  StreamWriter *writer;
4701
4635
 
4702
4636
  public:
4703
- StreamCompressor(StreamWriter *writer) : writer(writer) {}
4704
- virtual ~StreamCompressor() {}
4637
+ StreamEncoder(StreamWriter *writer) : writer(writer) {}
4638
+ virtual ~StreamEncoder() {}
4705
4639
 
4706
- virtual bool Init(CompressionType type, CompressionSpeed speed) = 0;
4707
4640
  virtual bool Write(Span<const uint8_t> buf) = 0;
4708
4641
  virtual bool Finalize() = 0;
4709
4642
 
@@ -4714,7 +4647,7 @@ protected:
4714
4647
  bool WriteRaw(Span<const uint8_t> buf) { return writer->WriteRaw(buf); }
4715
4648
  };
4716
4649
 
4717
- typedef StreamCompressor *CreateCompressorFunc(StreamWriter *writer);
4650
+ typedef StreamEncoder *CreateCompressorFunc(StreamWriter *writer, CompressionType type, CompressionSpeed speed);
4718
4651
 
4719
4652
  class StreamCompressorHelper {
4720
4653
  public:
@@ -4722,9 +4655,9 @@ public:
4722
4655
  };
4723
4656
 
4724
4657
  #define RG_REGISTER_COMPRESSOR(Type, Cls) \
4725
- static StreamCompressor *RG_UNIQUE_NAME(CreateCompressor)(StreamWriter *writer) \
4658
+ static StreamEncoder *RG_UNIQUE_NAME(CreateCompressor)(StreamWriter *writer, CompressionType type, CompressionSpeed speed) \
4726
4659
  { \
4727
- StreamCompressor *compressor = new Cls(writer); \
4660
+ StreamEncoder *compressor = new Cls(writer, type, speed); \
4728
4661
  return compressor; \
4729
4662
  } \
4730
4663
  static StreamCompressorHelper RG_UNIQUE_NAME(CreateCompressorHelper)((Type), RG_UNIQUE_NAME(CreateCompressor))
@@ -4934,6 +4867,36 @@ bool OptionToEnum(Span<const OptionDesc> options, Span<const char> str, T *out_v
4934
4867
  return false;
4935
4868
  }
4936
4869
 
4870
+ template <typename T>
4871
+ bool OptionToEnumI(Span<const char *const> options, Span<const char> str, T *out_value)
4872
+ {
4873
+ for (Size i = 0; i < options.len; i++) {
4874
+ const char *opt = options[i];
4875
+
4876
+ if (TestStrI(opt, str)) {
4877
+ *out_value = (T)i;
4878
+ return true;
4879
+ }
4880
+ }
4881
+
4882
+ return false;
4883
+ }
4884
+
4885
+ template <typename T>
4886
+ bool OptionToEnumI(Span<const OptionDesc> options, Span<const char> str, T *out_value)
4887
+ {
4888
+ for (Size i = 0; i < options.len; i++) {
4889
+ const OptionDesc &desc = options[i];
4890
+
4891
+ if (TestStrI(desc.name, str)) {
4892
+ *out_value = (T)i;
4893
+ return true;
4894
+ }
4895
+ }
4896
+
4897
+ return false;
4898
+ }
4899
+
4937
4900
  template <typename T>
4938
4901
  bool OptionToFlag(Span<const char *const> options, Span<const char> str, T *out_flags, bool enable = true)
4939
4902
  {
@@ -4964,6 +4927,36 @@ bool OptionToFlag(Span<const OptionDesc> options, Span<const char> str, T *out_f
4964
4927
  return false;
4965
4928
  }
4966
4929
 
4930
+ template <typename T>
4931
+ bool OptionToFlagI(Span<const char *const> options, Span<const char> str, T *out_flags, bool enable = true)
4932
+ {
4933
+ for (Size i = 0; i < options.len; i++) {
4934
+ const char *opt = options[i];
4935
+
4936
+ if (TestStrI(opt, str)) {
4937
+ *out_flags = ApplyMask(*out_flags, 1u << i, enable);
4938
+ return true;
4939
+ }
4940
+ }
4941
+
4942
+ return false;
4943
+ }
4944
+
4945
+ template <typename T>
4946
+ bool OptionToFlagI(Span<const OptionDesc> options, Span<const char> str, T *out_flags, bool enable = true)
4947
+ {
4948
+ for (Size i = 0; i < options.len; i++) {
4949
+ const OptionDesc &desc = options[i];
4950
+
4951
+ if (TestStrI(desc.name, str)) {
4952
+ *out_flags = ApplyMask(*out_flags, 1u << i, enable);
4953
+ return true;
4954
+ }
4955
+ }
4956
+
4957
+ return false;
4958
+ }
4959
+
4967
4960
  // ------------------------------------------------------------------------
4968
4961
  // Console prompter (simplified readline)
4969
4962
  // ------------------------------------------------------------------------
@@ -27,7 +27,7 @@
27
27
 
28
28
  namespace RG {
29
29
 
30
- class LZ4Decompressor: public StreamDecompressor {
30
+ class LZ4Decompressor: public StreamDecoder {
31
31
  LZ4F_dctx *decoder = nullptr;
32
32
  bool done = false;
33
33
 
@@ -39,25 +39,23 @@ class LZ4Decompressor: public StreamDecompressor {
39
39
  Size out_len = 0;
40
40
 
41
41
  public:
42
- LZ4Decompressor(StreamReader *reader) : StreamDecompressor(reader) {}
42
+ LZ4Decompressor(StreamReader *reader, CompressionType type);
43
43
  ~LZ4Decompressor();
44
44
 
45
- bool Init(CompressionType type) override;
46
45
  Size Read(Size max_len, void *out_buf) override;
47
46
  };
48
47
 
49
- LZ4Decompressor::~LZ4Decompressor()
50
- {
51
- LZ4F_freeDecompressionContext(decoder);
52
- }
53
-
54
- bool LZ4Decompressor::Init(CompressionType)
48
+ LZ4Decompressor::LZ4Decompressor(StreamReader *reader, CompressionType)
49
+ : StreamDecoder(reader)
55
50
  {
56
51
  LZ4F_errorCode_t err = LZ4F_createDecompressionContext(&decoder, LZ4F_VERSION);
57
52
  if (LZ4F_isError(err))
58
53
  throw std::bad_alloc();
54
+ }
59
55
 
60
- return true;
56
+ LZ4Decompressor::~LZ4Decompressor()
57
+ {
58
+ LZ4F_freeDecompressionContext(decoder);
61
59
  }
62
60
 
63
61
  Size LZ4Decompressor::Read(Size max_len, void *user_buf)
@@ -106,27 +104,22 @@ Size LZ4Decompressor::Read(Size max_len, void *user_buf)
106
104
  RG_UNREACHABLE();
107
105
  }
108
106
 
109
- class LZ4Compressor: public StreamCompressor {
107
+ class LZ4Compressor: public StreamEncoder {
110
108
  LZ4F_cctx *encoder = nullptr;
111
109
  LZ4F_preferences_t prefs = {};
112
110
 
113
111
  HeapArray<uint8_t> dynamic_buf;
114
112
 
115
113
  public:
116
- LZ4Compressor(StreamWriter *writer) : StreamCompressor(writer) {}
114
+ LZ4Compressor(StreamWriter *writer, CompressionType type, CompressionSpeed speed);
117
115
  ~LZ4Compressor();
118
116
 
119
- bool Init(CompressionType type, CompressionSpeed speed) override;
120
117
  bool Write(Span<const uint8_t> buf) override;
121
118
  bool Finalize() override;
122
119
  };
123
120
 
124
- LZ4Compressor::~LZ4Compressor()
125
- {
126
- LZ4F_freeCompressionContext(encoder);
127
- }
128
-
129
- bool LZ4Compressor::Init(CompressionType, CompressionSpeed speed)
121
+ LZ4Compressor::LZ4Compressor(StreamWriter *writer, CompressionType, CompressionSpeed speed)
122
+ : StreamEncoder(writer)
130
123
  {
131
124
  LZ4F_errorCode_t err = LZ4F_createCompressionContext(&encoder, LZ4F_VERSION);
132
125
  if (LZ4F_isError(err))
@@ -141,15 +134,15 @@ bool LZ4Compressor::Init(CompressionType, CompressionSpeed speed)
141
134
  dynamic_buf.Grow(LZ4F_HEADER_SIZE_MAX);
142
135
 
143
136
  size_t ret = LZ4F_compressBegin(encoder, dynamic_buf.end(), dynamic_buf.capacity - dynamic_buf.len, &prefs);
144
-
145
- if (LZ4F_isError(ret)) {
146
- LogError("Failed to start LZ4 stream for '%1': %2", GetFileName(), LZ4F_getErrorName(ret));
147
- return false;
148
- }
137
+ if (LZ4F_isError(ret))
138
+ throw std::bad_alloc();
149
139
 
150
140
  dynamic_buf.len += ret;
141
+ }
151
142
 
152
- return true;
143
+ LZ4Compressor::~LZ4Compressor()
144
+ {
145
+ LZ4F_freeCompressionContext(encoder);
153
146
  }
154
147
 
155
148
  bool LZ4Compressor::Write(Span<const uint8_t> buf)
@@ -26,7 +26,7 @@
26
26
 
27
27
  namespace RG {
28
28
 
29
- class MinizDecompressor: public StreamDecompressor {
29
+ class MinizDecompressor: public StreamDecoder {
30
30
  tinfl_decompressor inflator;
31
31
  bool done = false;
32
32
 
@@ -45,21 +45,19 @@ class MinizDecompressor: public StreamDecompressor {
45
45
  Size uncompressed_size = 0;
46
46
 
47
47
  public:
48
- MinizDecompressor(StreamReader *reader) : StreamDecompressor(reader) {}
48
+ MinizDecompressor(StreamReader *reader, CompressionType type);
49
49
  ~MinizDecompressor() {}
50
50
 
51
- bool Init(CompressionType type) override;
52
51
  Size Read(Size max_len, void *out_buf) override;
53
52
  };
54
53
 
55
- bool MinizDecompressor::Init(CompressionType type)
54
+ MinizDecompressor::MinizDecompressor(StreamReader *reader, CompressionType type)
55
+ : StreamDecoder(reader)
56
56
  {
57
57
  static_assert(RG_SIZE(out_buf) >= TINFL_LZ_DICT_SIZE);
58
58
 
59
59
  tinfl_init(&inflator);
60
60
  is_gzip = (type == CompressionType::Gzip);
61
-
62
- return true;
63
61
  }
64
62
 
65
63
  Size MinizDecompressor::Read(Size max_len, void *user_buf)
@@ -217,7 +215,7 @@ truncated_error:
217
215
  return -1;
218
216
  }
219
217
 
220
- class MinizCompressor: public StreamCompressor {
218
+ class MinizCompressor: public StreamEncoder {
221
219
  tdefl_compressor deflator;
222
220
 
223
221
  // Gzip support
@@ -229,10 +227,9 @@ class MinizCompressor: public StreamCompressor {
229
227
  LocalArray<uint8_t, 1024> small_buf;
230
228
 
231
229
  public:
232
- MinizCompressor(StreamWriter *writer) : StreamCompressor(writer) {}
230
+ MinizCompressor(StreamWriter *writer, CompressionType type, CompressionSpeed speed);
233
231
  ~MinizCompressor() {}
234
232
 
235
- bool Init(CompressionType type, CompressionSpeed speed) override;
236
233
  bool Write(Span<const uint8_t> buf) override;
237
234
  bool Finalize() override;
238
235
 
@@ -240,7 +237,8 @@ private:
240
237
  bool WriteDeflate(Span<const uint8_t> buf);
241
238
  };
242
239
 
243
- bool MinizCompressor::Init(CompressionType type, CompressionSpeed speed)
240
+ MinizCompressor::MinizCompressor(StreamWriter *writer, CompressionType type, CompressionSpeed speed)
241
+ : StreamEncoder(writer)
244
242
  {
245
243
  is_gzip = (type == CompressionType::Gzip);
246
244
 
@@ -256,10 +254,7 @@ bool MinizCompressor::Init(CompressionType type, CompressionSpeed speed)
256
254
  MinizCompressor *compressor = (MinizCompressor *)udata;
257
255
  return (int)compressor->WriteRaw(MakeSpan((uint8_t *)buf, len));
258
256
  }, this, flags);
259
- if (status != TDEFL_STATUS_OKAY) {
260
- LogError("Failed to initialize Deflate compression for '%1'", GetFileName());
261
- return false;
262
- }
257
+ RG_ASSERT(status == TDEFL_STATUS_OKAY);
263
258
 
264
259
  if (is_gzip) {
265
260
  static uint8_t gzip_header[] = {
@@ -271,11 +266,8 @@ bool MinizCompressor::Init(CompressionType type, CompressionSpeed speed)
271
266
  0 // OS
272
267
  };
273
268
 
274
- if (!WriteRaw(gzip_header))
275
- return false;
269
+ WriteRaw(gzip_header);
276
270
  }
277
-
278
- return true;
279
271
  }
280
272
 
281
273
  bool MinizCompressor::Write(Span<const uint8_t> buf)