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.
- package/.claude/settings.local.json +92 -0
- package/.gitattributes +3 -0
- package/.release-it.json +6 -0
- package/CHANGELOG.md +209 -0
- package/History.md +23 -1
- package/README.md +750 -30
- package/RELEASING.md +131 -0
- package/binding.gyp +159 -438
- package/biome.json +81 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/errors.ts.html +586 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +146 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/errors.ts.html +586 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +146 -0
- package/coverage/lcov-report/lzma.ts.html +2596 -0
- package/coverage/lcov-report/pool.ts.html +769 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +636 -0
- package/coverage/lzma.ts.html +2596 -0
- package/coverage/pool.ts.html +769 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage-reports/assets/monocart-coverage-app.js +2 -0
- package/coverage-reports/coverage-data.js +1 -0
- package/coverage-reports/index.html +48 -0
- package/err.log +26 -0
- package/index.d.ts +254 -0
- package/lib/errors.d.ts +72 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +153 -0
- package/lib/errors.js.map +1 -0
- package/lib/lzma.d.ts +245 -0
- package/lib/lzma.d.ts.map +1 -0
- package/lib/lzma.js +626 -345
- package/lib/lzma.js.map +1 -0
- package/lib/pool.d.ts +123 -0
- package/lib/pool.d.ts.map +1 -0
- package/lib/pool.js +188 -0
- package/lib/pool.js.map +1 -0
- package/lib/types.d.ts +27 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +5 -0
- package/lib/types.js.map +1 -0
- package/package.json +60 -21
- package/pnpm-workspace.yaml +3 -0
- package/scripts/analyze-coverage.js +132 -0
- package/scripts/build_xz_with_cmake.py +390 -0
- package/scripts/compare-coverage-tools.js +93 -0
- package/scripts/copy_dll.py +51 -0
- package/scripts/download_xz_from_github.py +375 -0
- package/src/bindings/module.cpp +107 -0
- package/src/bindings/node-liblzma.cpp +522 -0
- package/src/bindings/node-liblzma.hpp +144 -0
- package/src/errors.ts +167 -0
- package/src/lzma.ts +839 -0
- package/src/pool.ts +228 -0
- package/src/types.ts +30 -0
- package/tsconfig.json +50 -0
- package/vitest.config.istanbul.ts +29 -0
- package/vitest.config.monocart.ts +44 -0
- package/vitest.config.ts +44 -0
- package/xz-version.json +8 -0
- package/prebuilds/darwin-x64/node.napi.node +0 -0
- package/prebuilds/linux-x64/node.napi.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- 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
|