node-liblzma 1.1.8 → 2.0.0

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 (76) hide show
  1. package/.claude/settings.local.json +92 -0
  2. package/.gitattributes +3 -0
  3. package/.release-it.json +6 -0
  4. package/CHANGELOG.md +209 -0
  5. package/History.md +23 -1
  6. package/README.md +750 -30
  7. package/RELEASING.md +131 -0
  8. package/binding.gyp +159 -438
  9. package/biome.json +81 -0
  10. package/coverage/base.css +224 -0
  11. package/coverage/block-navigation.js +87 -0
  12. package/coverage/errors.ts.html +586 -0
  13. package/coverage/favicon.png +0 -0
  14. package/coverage/index.html +146 -0
  15. package/coverage/lcov-report/base.css +224 -0
  16. package/coverage/lcov-report/block-navigation.js +87 -0
  17. package/coverage/lcov-report/errors.ts.html +586 -0
  18. package/coverage/lcov-report/favicon.png +0 -0
  19. package/coverage/lcov-report/index.html +146 -0
  20. package/coverage/lcov-report/lzma.ts.html +2596 -0
  21. package/coverage/lcov-report/pool.ts.html +769 -0
  22. package/coverage/lcov-report/prettify.css +1 -0
  23. package/coverage/lcov-report/prettify.js +2 -0
  24. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  25. package/coverage/lcov-report/sorter.js +210 -0
  26. package/coverage/lcov.info +636 -0
  27. package/coverage/lzma.ts.html +2596 -0
  28. package/coverage/pool.ts.html +769 -0
  29. package/coverage/prettify.css +1 -0
  30. package/coverage/prettify.js +2 -0
  31. package/coverage/sort-arrow-sprite.png +0 -0
  32. package/coverage/sorter.js +210 -0
  33. package/coverage-reports/assets/monocart-coverage-app.js +2 -0
  34. package/coverage-reports/coverage-data.js +1 -0
  35. package/coverage-reports/index.html +48 -0
  36. package/err.log +26 -0
  37. package/index.d.ts +254 -0
  38. package/lib/errors.d.ts +72 -0
  39. package/lib/errors.d.ts.map +1 -0
  40. package/lib/errors.js +153 -0
  41. package/lib/errors.js.map +1 -0
  42. package/lib/lzma.d.ts +245 -0
  43. package/lib/lzma.d.ts.map +1 -0
  44. package/lib/lzma.js +626 -345
  45. package/lib/lzma.js.map +1 -0
  46. package/lib/pool.d.ts +123 -0
  47. package/lib/pool.d.ts.map +1 -0
  48. package/lib/pool.js +188 -0
  49. package/lib/pool.js.map +1 -0
  50. package/lib/types.d.ts +27 -0
  51. package/lib/types.d.ts.map +1 -0
  52. package/lib/types.js +5 -0
  53. package/lib/types.js.map +1 -0
  54. package/package.json +60 -21
  55. package/pnpm-workspace.yaml +3 -0
  56. package/scripts/analyze-coverage.js +132 -0
  57. package/scripts/build_xz_with_cmake.py +390 -0
  58. package/scripts/compare-coverage-tools.js +93 -0
  59. package/scripts/copy_dll.py +51 -0
  60. package/scripts/download_xz_from_github.py +375 -0
  61. package/src/bindings/module.cpp +107 -0
  62. package/src/bindings/node-liblzma.cpp +522 -0
  63. package/src/bindings/node-liblzma.hpp +144 -0
  64. package/src/errors.ts +167 -0
  65. package/src/lzma.ts +839 -0
  66. package/src/pool.ts +228 -0
  67. package/src/types.ts +30 -0
  68. package/tsconfig.json +50 -0
  69. package/vitest.config.istanbul.ts +29 -0
  70. package/vitest.config.monocart.ts +44 -0
  71. package/vitest.config.ts +44 -0
  72. package/xz-version.json +8 -0
  73. package/prebuilds/darwin-x64/node.napi.node +0 -0
  74. package/prebuilds/linux-x64/node.napi.node +0 -0
  75. package/prebuilds/win32-x64/node.napi.node +0 -0
  76. package/scripts/download_extract_deps.py +0 -29
@@ -0,0 +1,522 @@
1
+ /**
2
+ * node-liblzma - Node.js bindings for liblzma
3
+ * Copyright (C) Olivier Orabona <olivier.orabona@gmail.com>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Lesser General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ **/
18
+
19
+ #include <node_buffer.h>
20
+ #include <vector>
21
+ #include "node-liblzma.hpp"
22
+
23
+ Napi::Value LZMA::Close(const Napi::CallbackInfo &info)
24
+ {
25
+ Napi::Env env = info.Env();
26
+
27
+ return LZMA::Close(env);
28
+ }
29
+
30
+ Napi::Value LZMA::Close(const Napi::Env &env)
31
+ {
32
+ Napi::MemoryManagement::AdjustExternalMemory(env, -int64_t(sizeof(LZMA)));
33
+
34
+ if (_wip)
35
+ {
36
+ _pending_close = true;
37
+ return env.Undefined();
38
+ }
39
+
40
+ _pending_close = false;
41
+
42
+ lzma_end(&_stream);
43
+
44
+ return env.Undefined();
45
+ }
46
+
47
+ void LZMA::Init(Napi::Env env, Napi::Object exports)
48
+ {
49
+ Napi::Function func =
50
+ DefineClass(env,
51
+ "LZMA",
52
+ {InstanceMethod("code", &LZMA::Code<true>),
53
+ InstanceMethod("codeSync", &LZMA::Code<false>),
54
+ InstanceMethod("close", &LZMA::Close)});
55
+
56
+ auto constructor = std::make_unique<Napi::FunctionReference>();
57
+ *constructor = Napi::Persistent(func);
58
+
59
+ // SetInstanceData with automatic cleanup (node-addon-api v8.5.0+)
60
+ env.SetInstanceData<Napi::FunctionReference>(constructor.release());
61
+
62
+ exports.Set("LZMA", func);
63
+ }
64
+
65
+ LZMA::LZMA(const Napi::CallbackInfo &info) : Napi::ObjectWrap<LZMA>(info), _stream(LZMA_STREAM_INIT),
66
+ _wip(false), _pending_close(false), _worker(nullptr)
67
+ {
68
+ Napi::Env env = info.Env();
69
+
70
+ // Validate constructor arguments
71
+ uint32_t mode;
72
+ Napi::Object opts;
73
+ if (!ValidateConstructorArgs(info, mode, opts))
74
+ {
75
+ return;
76
+ }
77
+
78
+ // Validate and extract options
79
+ Napi::Value optsCheck = opts.Get("check");
80
+ if (!optsCheck.IsNumber())
81
+ {
82
+ Napi::TypeError::New(env, "Expected 'check' to be an integer").ThrowAsJavaScriptException();
83
+ return;
84
+ }
85
+ lzma_check check = static_cast<lzma_check>(optsCheck.ToNumber().Uint32Value());
86
+
87
+ Napi::Value optsPreset = opts.Get("preset");
88
+ if (!optsPreset.IsNumber())
89
+ {
90
+ Napi::TypeError::New(env, "Expected 'preset' to be an integer").ThrowAsJavaScriptException();
91
+ return;
92
+ }
93
+ uint32_t preset = optsPreset.ToNumber().Uint32Value();
94
+
95
+ // Initialize filters
96
+ if (!InitializeFilters(opts, preset))
97
+ {
98
+ return;
99
+ }
100
+
101
+ // Initialize encoder or decoder based on mode
102
+ bool success = false;
103
+ switch (mode)
104
+ {
105
+ case STREAM_DECODE:
106
+ success = InitializeDecoder();
107
+ break;
108
+ case STREAM_ENCODE:
109
+ success = InitializeEncoder(opts, preset, check);
110
+ break;
111
+ default:
112
+ Napi::Error::New(env, "Invalid stream mode").ThrowAsJavaScriptException();
113
+ return;
114
+ }
115
+
116
+ if (!success)
117
+ {
118
+ return;
119
+ }
120
+
121
+ // Register external memory
122
+ Napi::MemoryManagement::AdjustExternalMemory(env, sizeof(LZMA));
123
+ }
124
+
125
+ LZMA::~LZMA()
126
+ {
127
+ // Release any Buffer references
128
+ if (!_in_buf_ref.IsEmpty())
129
+ _in_buf_ref.Reset();
130
+ if (!_out_buf_ref.IsEmpty())
131
+ _out_buf_ref.Reset();
132
+
133
+ // Cleanup worker if still active
134
+ if (_worker != nullptr)
135
+ {
136
+ _worker = nullptr; // Worker will clean itself up
137
+ }
138
+
139
+ // Smart pointer will automatically cleanup filter array
140
+ // filters.reset() is called automatically
141
+
142
+ // Ensure LZMA stream is properly cleaned up
143
+ lzma_end(&_stream);
144
+ }
145
+
146
+ /**<
147
+ * \brief Do the encoding/decoding with async support
148
+ *
149
+ * Function prototype is (sync):
150
+ * .codeSync flushFlag, input_buffer, input_offset, output_buffer, output_offset, callback
151
+ * Function prototype is (async):
152
+ * .code flushFlag, input_buffer, input_offset, availInBefore, output_buffer, output_offset, callback
153
+ *
154
+ * Where:
155
+ * flushFlag: type: Uint32
156
+ * input_buffer: type: Buffer
157
+ * input_offset: type: Uint32
158
+ * availInBefore: type: Uint32
159
+ * output_buffer: type: Buffer
160
+ * output_offset: type: Uint32
161
+ * callback: type: Function
162
+ */
163
+ template <bool async>
164
+ Napi::Value LZMA::Code(const Napi::CallbackInfo &info)
165
+ {
166
+ // Setup with manual guard (safer than RAII for JS exceptions)
167
+ this->_wip = true;
168
+ this->Ref();
169
+
170
+ // Validate and prepare buffers
171
+ BufferContext ctx;
172
+ if (!ValidateAndPrepareBuffers<async>(info, ctx))
173
+ {
174
+ this->_wip = false;
175
+ this->Unref();
176
+ return info.Env().Undefined();
177
+ }
178
+
179
+ // Configure stream with prepared buffers
180
+ this->_stream.next_in = ctx.in;
181
+ this->_stream.avail_in = ctx.in_len;
182
+ this->_stream.next_out = ctx.out;
183
+ this->_stream.avail_out = ctx.out_len;
184
+
185
+ // Execute based on mode with minimal branching
186
+ if constexpr (async)
187
+ {
188
+ return StartAsyncWork(info);
189
+ }
190
+ else
191
+ {
192
+ return ExecuteSyncWork(info);
193
+ }
194
+ }
195
+
196
+ void LZMA::Process(LZMA *obj)
197
+ {
198
+ // the real work is done here :)
199
+ // Note: _wip should already be set by the caller
200
+ obj->_ret = lzma_code(&(obj->_stream), obj->_action);
201
+ }
202
+
203
+ Napi::Value LZMA::AfterSync(const Napi::CallbackInfo &info, LZMA *obj)
204
+ {
205
+ Napi::Env env = info.Env();
206
+ return obj->After(env);
207
+ }
208
+
209
+ template <bool async>
210
+ bool LZMA::ValidateAndPrepareBuffers(const Napi::CallbackInfo &info, BufferContext &ctx)
211
+ {
212
+ Napi::Env env = info.Env();
213
+ constexpr int expected_args = async ? ASYNC_PARAM_COUNT : SYNC_PARAM_COUNT;
214
+ // Maximum buffer size: 512MB to prevent DoS via resource exhaustion
215
+ constexpr size_t MAX_BUFFER_SIZE = 512UL * 1024 * 1024;
216
+
217
+ // Validate parameter count
218
+ if (info.Length() != expected_args)
219
+ {
220
+ std::string msg = async
221
+ ? "Invalid arguments: LZMA::Code requires 7 arguments (flushFlag, input_buffer, input_offset, availInBefore, output_buffer, output_offset, callback)"
222
+ : "Invalid arguments: LZMA::Code requires 6 arguments (flushFlag, input_buffer, input_offset, availInBefore, output_buffer, output_offset)";
223
+ Napi::Error::New(env, msg).ThrowAsJavaScriptException();
224
+ return false;
225
+ }
226
+
227
+ // Validate flush flag
228
+ if (!info[0].IsNumber())
229
+ {
230
+ Napi::Error::New(env, "flushFlag must be an integer").ThrowAsJavaScriptException();
231
+ return false;
232
+ }
233
+ this->_action = static_cast<lzma_action>(info[0].ToNumber().Uint32Value());
234
+
235
+ // Handle input buffer (can be null for flush operations)
236
+ if (info[1].IsNull())
237
+ {
238
+ ctx.in = nullptr;
239
+ ctx.in_len = 0;
240
+ ctx.in_off = 0;
241
+ }
242
+ else
243
+ {
244
+ if (!info[1].IsBuffer())
245
+ {
246
+ Napi::TypeError::New(env, "Invalid argument: 'input_buffer' must be a Buffer").ThrowAsJavaScriptException();
247
+ return false;
248
+ }
249
+
250
+ uint8_t *in_buf = info[1].As<Napi::Buffer<uint8_t>>().Data();
251
+ ctx.in_off = info[2].ToNumber().Uint32Value();
252
+ ctx.in_len = info[3].ToNumber().Uint32Value();
253
+ size_t in_max = info[1].As<Napi::Buffer<uint8_t>>().Length();
254
+
255
+ // Validate buffer size limit
256
+ if (in_max > MAX_BUFFER_SIZE)
257
+ {
258
+ Napi::RangeError::New(env, "Input buffer exceeds maximum size of 512MB").ThrowAsJavaScriptException();
259
+ return false;
260
+ }
261
+
262
+ if (!node::Buffer::IsWithinBounds(ctx.in_off, ctx.in_len, in_max))
263
+ {
264
+ Napi::Error::New(env, "Input offset out of bounds!").ThrowAsJavaScriptException();
265
+ return false;
266
+ }
267
+ ctx.in = in_buf + ctx.in_off;
268
+ }
269
+
270
+ // Handle output buffer (required)
271
+ if (!info[4].IsBuffer())
272
+ {
273
+ Napi::TypeError::New(env, "Invalid argument: 'output_buffer' must be a Buffer").ThrowAsJavaScriptException();
274
+ return false;
275
+ }
276
+
277
+ uint8_t *out_buf = info[4].As<Napi::Buffer<uint8_t>>().Data();
278
+ size_t out_max = info[4].As<Napi::Buffer<uint8_t>>().Length();
279
+
280
+ // Validate output buffer size limit
281
+ if (out_max > MAX_BUFFER_SIZE)
282
+ {
283
+ Napi::RangeError::New(env, "Output buffer exceeds maximum size of 512MB").ThrowAsJavaScriptException();
284
+ return false;
285
+ }
286
+
287
+ ctx.out_off = info[5].ToNumber().Uint32Value();
288
+ ctx.out_len = out_max - ctx.out_off;
289
+ ctx.out = out_buf + ctx.out_off;
290
+
291
+ // Validate callback for async mode
292
+ if constexpr (async)
293
+ {
294
+ if (!info[6].IsFunction())
295
+ {
296
+ Napi::TypeError::New(env, "Invalid argument: 'callback' must be a Function").ThrowAsJavaScriptException();
297
+ return false;
298
+ }
299
+ }
300
+
301
+ return true;
302
+ }
303
+
304
+ Napi::Value LZMA::StartAsyncWork(const Napi::CallbackInfo &info)
305
+ {
306
+ // Persist buffers so V8 GC can't free them while the async worker runs
307
+ if (!info[1].IsNull() && info[1].IsBuffer())
308
+ {
309
+ this->_in_buf_ref = Napi::Persistent(info[1].As<Napi::Buffer<uint8_t>>());
310
+ }
311
+ this->_out_buf_ref = Napi::Persistent(info[4].As<Napi::Buffer<uint8_t>>());
312
+
313
+ // Use the real callback for the worker to own and call
314
+ this->_worker = new LZMAWorker(info.Env(), this, info[6].As<Napi::Function>());
315
+ this->_worker->Queue();
316
+
317
+ return info.Env().Undefined();
318
+ }
319
+
320
+ Napi::Value LZMA::ExecuteSyncWork(const Napi::CallbackInfo &info)
321
+ {
322
+ Process(this);
323
+ return AfterSync(info, this);
324
+ }
325
+
326
+ bool LZMA::ValidateConstructorArgs(const Napi::CallbackInfo &info, uint32_t &mode, Napi::Object &opts)
327
+ {
328
+ Napi::Env env = info.Env();
329
+
330
+ if (info.Length() != 2)
331
+ {
332
+ Napi::TypeError::New(env, "Wrong number of arguments, expected mode(int) and opts(object)").ThrowAsJavaScriptException();
333
+ return false;
334
+ }
335
+
336
+ if (!info[0].IsNumber())
337
+ {
338
+ Napi::TypeError::New(env, "Expected mode to be an integer").ThrowAsJavaScriptException();
339
+ return false;
340
+ }
341
+ mode = info[0].ToNumber().Uint32Value();
342
+
343
+ if (!info[1].IsObject())
344
+ {
345
+ Napi::TypeError::New(env, "Expected object as second argument").ThrowAsJavaScriptException();
346
+ return false;
347
+ }
348
+ opts = info[1].ToObject();
349
+
350
+ return true;
351
+ }
352
+
353
+ bool LZMA::InitializeFilters(const Napi::Object &opts, uint32_t preset)
354
+ {
355
+ Napi::Env env = opts.Env();
356
+
357
+ // Validate and get filters array
358
+ Napi::Value optsFilters = opts.Get("filters");
359
+ if (!optsFilters.IsArray())
360
+ {
361
+ Napi::TypeError::New(env, "Expected 'filters' to be an array").ThrowAsJavaScriptException();
362
+ return false;
363
+ }
364
+
365
+ Napi::Array filters_handle = optsFilters.As<Napi::Array>();
366
+ uint32_t filters_len = filters_handle.Length();
367
+
368
+ // Validate filter count
369
+ if (filters_len > LZMA_FILTERS_MAX - 1)
370
+ {
371
+ Napi::RangeError::New(env, "More filters than allowed maximum").ThrowAsJavaScriptException();
372
+ return false;
373
+ }
374
+
375
+ // Initialize LZMA2 options from preset
376
+ if (lzma_lzma_preset(&this->_opt_lzma2, preset))
377
+ {
378
+ Napi::Error::New(env, "Unsupported preset, possibly a bug").ThrowAsJavaScriptException();
379
+ return false;
380
+ }
381
+
382
+ // Allocate filter array with smart pointer
383
+ this->filters = std::make_unique<lzma_filter[]>(filters_len + 1);
384
+
385
+ // Configure filters
386
+ for (uint32_t i = 0; i < filters_len; ++i)
387
+ {
388
+ Napi::Value filter = filters_handle.Get(i);
389
+ if (!filter.IsNumber())
390
+ {
391
+ Napi::Error::New(env, "Filter must be an integer").ThrowAsJavaScriptException();
392
+ return false;
393
+ }
394
+
395
+ uint64_t current = filter.ToNumber().Uint32Value();
396
+ this->filters[i].id = current;
397
+ if (current == LZMA_FILTER_LZMA2)
398
+ {
399
+ this->filters[i].options = &this->_opt_lzma2;
400
+ }
401
+ else
402
+ {
403
+ this->filters[i].options = nullptr;
404
+ }
405
+ }
406
+
407
+ // Set terminator
408
+ this->filters[filters_len] = {.id = LZMA_VLI_UNKNOWN, .options = nullptr};
409
+
410
+ return true;
411
+ }
412
+
413
+ bool LZMA::InitializeEncoder(const Napi::Object &opts, uint32_t preset, lzma_check check)
414
+ {
415
+ Napi::Env env = opts.Env();
416
+
417
+ // Validate threads parameter
418
+ Napi::Value optsThreads = opts.Get("threads");
419
+ if (!optsThreads.IsNumber())
420
+ {
421
+ Napi::Error::New(env, "Threads must be an integer").ThrowAsJavaScriptException();
422
+ return false;
423
+ }
424
+
425
+ #ifdef ENABLE_THREAD_SUPPORT
426
+ unsigned int threads = optsThreads.ToNumber().Uint32Value();
427
+ #pragma GCC diagnostic push
428
+ #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
429
+
430
+ lzma_mt mt = {
431
+ .flags = 0,
432
+ .threads = threads,
433
+ .block_size = 0,
434
+ .timeout = 0,
435
+ .preset = preset,
436
+ .filters = this->filters.get(),
437
+ .check = check,
438
+ };
439
+
440
+ #pragma GCC diagnostic pop
441
+
442
+ lzma_ret ret;
443
+ if (threads > 1)
444
+ {
445
+ ret = lzma_stream_encoder_mt(&this->_stream, &mt);
446
+ }
447
+ else
448
+ {
449
+ ret = lzma_stream_encoder(&this->_stream, this->filters.get(), check);
450
+ }
451
+ #else
452
+ lzma_ret ret = lzma_stream_encoder(&this->_stream, this->filters.get(), check);
453
+ #endif
454
+
455
+ if (ret != LZMA_OK)
456
+ {
457
+ Napi::Error::New(env, "LZMA encoder failure, returned " + std::to_string(ret)).ThrowAsJavaScriptException();
458
+ return false;
459
+ }
460
+
461
+ return true;
462
+ }
463
+
464
+ bool LZMA::InitializeDecoder()
465
+ {
466
+ lzma_ret ret = lzma_stream_decoder(&this->_stream, UINT64_MAX, LZMA_CONCATENATED);
467
+ return ret == LZMA_OK;
468
+ }
469
+
470
+ void LZMA::AfterCommon(const Napi::Env &env)
471
+ {
472
+ // Mark work done
473
+ this->_wip = false;
474
+
475
+ // Clear worker pointer if any
476
+ _worker = nullptr;
477
+
478
+ // Release any Buffer references (async path uses these)
479
+ if (!_in_buf_ref.IsEmpty())
480
+ _in_buf_ref.Reset();
481
+ if (!_out_buf_ref.IsEmpty())
482
+ _out_buf_ref.Reset();
483
+
484
+ // Balance reference taken in Code()
485
+ Unref();
486
+
487
+ // Honor pending close requests
488
+ if (_pending_close)
489
+ {
490
+ Close(env);
491
+ }
492
+ }
493
+
494
+ Napi::Array LZMA::After(const Napi::Env &env)
495
+ {
496
+ Napi::HandleScope scope(env);
497
+ Napi::Number ret = Napi::Number::New(env, this->_ret);
498
+ Napi::Number avail_in = Napi::Number::New(env, this->_stream.avail_in);
499
+ Napi::Number avail_out = Napi::Number::New(env, this->_stream.avail_out);
500
+
501
+ Napi::Array result = Napi::Array::New(env, 3);
502
+ result[(uint32_t)0] = ret;
503
+ result[(uint32_t)1] = avail_in;
504
+ result[(uint32_t)2] = avail_out;
505
+
506
+ AfterCommon(env);
507
+
508
+ return result;
509
+ }
510
+
511
+ void LZMA::After(const Napi::Env &env, const Napi::Function &cb)
512
+ {
513
+ Napi::HandleScope scope(env);
514
+ Napi::Number ret = Napi::Number::New(env, this->_ret);
515
+ Napi::Number avail_in = Napi::Number::New(env, this->_stream.avail_in);
516
+ Napi::Number avail_out = Napi::Number::New(env, this->_stream.avail_out);
517
+
518
+ // Call the provided JS callback with the three numeric results
519
+ cb.Call({ret, avail_in, avail_out});
520
+
521
+ AfterCommon(env);
522
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * node-liblzma - Node.js bindings for liblzma
3
+ * Copyright (C) 2014-2015 Olivier Orabona <olivier.orabona@gmail.com>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Lesser General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ **/
18
+
19
+ #ifndef BUILDING_NODE_EXTENSION
20
+ #define BUILDING_NODE_EXTENSION
21
+ #endif
22
+
23
+ #ifndef NODE_LIBLZMA_H
24
+ #define NODE_LIBLZMA_H
25
+
26
+ #include <lzma.h>
27
+ #include <napi.h>
28
+
29
+ #include <sstream>
30
+ #include <memory>
31
+ #include <functional>
32
+
33
+ constexpr unsigned int STREAM_ENCODE = 0;
34
+ constexpr unsigned int STREAM_DECODE = 1;
35
+ constexpr int SYNC_PARAM_COUNT = 6;
36
+ constexpr int ASYNC_PARAM_COUNT = 7;
37
+
38
+ #ifdef ENABLE_THREAD_SUPPORT
39
+ constexpr bool HAS_THREADS_SUPPORT = true;
40
+ #else
41
+ constexpr bool HAS_THREADS_SUPPORT = false;
42
+ #endif
43
+
44
+ class LZMAWorker;
45
+ class LZMA : public Napi::ObjectWrap<LZMA>
46
+ {
47
+ public:
48
+ static void Init(Napi::Env env, Napi::Object exports);
49
+
50
+ explicit LZMA(const Napi::CallbackInfo &info);
51
+ ~LZMA();
52
+
53
+ Napi::Value Close(const Napi::CallbackInfo &info);
54
+ Napi::Value Close(const Napi::Env &env);
55
+
56
+ template <bool async>
57
+ Napi::Value Code(const Napi::CallbackInfo &info);
58
+
59
+ static void Process(LZMA *obj);
60
+ static Napi::Value AfterSync(const Napi::CallbackInfo &info, LZMA *obj);
61
+
62
+ // Allow LZMAWorker access to private members
63
+ friend class LZMAWorker;
64
+
65
+ private:
66
+ // Buffer preparation and validation
67
+ struct BufferContext
68
+ {
69
+ const uint8_t *in;
70
+ uint8_t *out;
71
+ size_t in_len, out_len, in_off, out_off;
72
+ };
73
+
74
+ template <bool async>
75
+ bool ValidateAndPrepareBuffers(const Napi::CallbackInfo &info, BufferContext &ctx);
76
+
77
+ Napi::Value StartAsyncWork(const Napi::CallbackInfo &info);
78
+ Napi::Value ExecuteSyncWork(const Napi::CallbackInfo &info);
79
+
80
+ // Constructor helpers
81
+ bool ValidateConstructorArgs(const Napi::CallbackInfo &info, uint32_t &mode, Napi::Object &opts);
82
+ bool InitializeFilters(const Napi::Object &opts, uint32_t preset);
83
+ bool InitializeEncoder(const Napi::Object &opts, uint32_t preset, lzma_check check);
84
+ bool InitializeDecoder();
85
+
86
+ // Common cleanup operations for both sync and async completion
87
+ void AfterCommon(const Napi::Env &env);
88
+
89
+ // Unified completion helpers (overloaded):
90
+ // - After(env): builds result array, marks done, cleans up, returns array (sync path)
91
+ // - After(env, cb): builds result array, marks done, calls cb, then cleans up (async path)
92
+ Napi::Array After(const Napi::Env &env);
93
+ void After(const Napi::Env &env, const Napi::Function &cb);
94
+
95
+ lzma_stream _stream;
96
+ bool _wip;
97
+ bool _pending_close;
98
+
99
+ LZMAWorker *_worker;
100
+
101
+ lzma_action _action;
102
+ lzma_ret _ret;
103
+ std::unique_ptr<lzma_filter[]> filters;
104
+ // Persist LZMA2 options referenced by filters to avoid dangling pointer
105
+ lzma_options_lzma _opt_lzma2;
106
+
107
+ // Keep input/output Buffers alive while async work runs
108
+ Napi::Reference<Napi::Buffer<uint8_t>> _in_buf_ref;
109
+ Napi::Reference<Napi::Buffer<uint8_t>> _out_buf_ref;
110
+ };
111
+
112
+ class LZMAWorker : public Napi::AsyncWorker
113
+ {
114
+ public:
115
+ LZMAWorker(Napi::Env env, LZMA *instance, Napi::Function cb)
116
+ : AsyncWorker(cb, "LZMAWorker"), lzma(instance)
117
+ {
118
+ // Set work in progress flag when worker is created
119
+ lzma->_wip = true;
120
+ }
121
+ ~LZMAWorker() = default;
122
+
123
+ void Execute() override
124
+ {
125
+ // Perform the compression/decompression step
126
+ LZMA::Process(this->lzma);
127
+ }
128
+ void OnOK() override
129
+ {
130
+ lzma->After(Env(), Callback().Value());
131
+ }
132
+ void OnError(const Napi::Error & /*e*/) override
133
+ {
134
+ // Fallback to a generic programming error code
135
+ // Set a generic error code so BuildResult exposes it
136
+ lzma->_ret = LZMA_PROG_ERROR;
137
+ lzma->After(Env(), Callback().Value());
138
+ }
139
+
140
+ private:
141
+ LZMA *lzma;
142
+ };
143
+
144
+ #endif // NODE_LIBLZMA_H