node-liblzma 1.1.9 → 2.0.3
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/.gitattributes +3 -0
- package/.release-it.json +7 -0
- package/.release-it.manual.json +7 -0
- package/.release-it.retry.json +3 -0
- package/CHANGELOG.md +271 -0
- package/History.md +20 -0
- package/README.md +750 -30
- package/RELEASING.md +131 -0
- package/binding.gyp +162 -436
- package/biome.json +81 -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 +61 -22
- package/pnpm-workspace.yaml +3 -0
- package/prebuilds/darwin-x64/node-liblzma.node +0 -0
- package/prebuilds/linux-x64/node-liblzma.node +0 -0
- package/prebuilds/win32-x64/node-liblzma.node +0 -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 +376 -0
- package/src/bindings/node-liblzma.cpp +411 -229
- package/src/bindings/node-liblzma.hpp +101 -48
- 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 +52 -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
- package/scripts/prebuildify.py +0 -13
- package/src/lzma.coffee +0 -344
|
@@ -1,34 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
18
|
|
|
19
|
-
#include "node-liblzma.hpp"
|
|
20
19
|
#include <node_buffer.h>
|
|
20
|
+
#include <vector>
|
|
21
|
+
#include "node-liblzma.hpp"
|
|
21
22
|
|
|
22
|
-
Napi::Value LZMA::Close(const Napi::CallbackInfo &info)
|
|
23
|
+
Napi::Value LZMA::Close(const Napi::CallbackInfo &info)
|
|
24
|
+
{
|
|
23
25
|
Napi::Env env = info.Env();
|
|
24
26
|
|
|
25
27
|
return LZMA::Close(env);
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
Napi::Value LZMA::Close(const Napi::Env &env)
|
|
30
|
+
Napi::Value LZMA::Close(const Napi::Env &env)
|
|
31
|
+
{
|
|
29
32
|
Napi::MemoryManagement::AdjustExternalMemory(env, -int64_t(sizeof(LZMA)));
|
|
30
33
|
|
|
31
|
-
if(_wip)
|
|
34
|
+
if (_wip)
|
|
35
|
+
{
|
|
32
36
|
_pending_close = true;
|
|
33
37
|
return env.Undefined();
|
|
34
38
|
}
|
|
@@ -40,301 +44,479 @@ Napi::Value LZMA::Close(const Napi::Env &env) {
|
|
|
40
44
|
return env.Undefined();
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
void LZMA::Init(Napi::Env env, Napi::Object exports)
|
|
47
|
+
void LZMA::Init(Napi::Env env, Napi::Object exports)
|
|
48
|
+
{
|
|
44
49
|
Napi::Function func =
|
|
45
50
|
DefineClass(env,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Napi::FunctionReference* constructor = new Napi::FunctionReference();
|
|
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>();
|
|
54
57
|
*constructor = Napi::Persistent(func);
|
|
55
|
-
|
|
58
|
+
|
|
59
|
+
// SetInstanceData with automatic cleanup (node-addon-api v8.5.0+)
|
|
60
|
+
env.SetInstanceData<Napi::FunctionReference>(constructor.release());
|
|
56
61
|
|
|
57
62
|
exports.Set("LZMA", func);
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
LZMA::LZMA(const Napi::CallbackInfo&
|
|
61
|
-
|
|
65
|
+
LZMA::LZMA(const Napi::CallbackInfo &info) : Napi::ObjectWrap<LZMA>(info), _stream(LZMA_STREAM_INIT),
|
|
66
|
+
_wip(false), _pending_close(false), _worker(nullptr)
|
|
62
67
|
{
|
|
63
68
|
Napi::Env env = info.Env();
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
uint32_t mode = info[0].ToNumber().Uint32Value();
|
|
71
|
-
|
|
72
|
-
if (!info[1].IsObject()) {
|
|
73
|
-
Napi::TypeError::New(env, "Expected object as second argument").ThrowAsJavaScriptException();
|
|
70
|
+
// Validate constructor arguments
|
|
71
|
+
uint32_t mode;
|
|
72
|
+
Napi::Object opts;
|
|
73
|
+
if (!ValidateConstructorArgs(info, mode, opts))
|
|
74
|
+
{
|
|
74
75
|
return;
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
// Validate and extract options
|
|
79
79
|
Napi::Value optsCheck = opts.Get("check");
|
|
80
|
-
if (!optsCheck.IsNumber())
|
|
80
|
+
if (!optsCheck.IsNumber())
|
|
81
|
+
{
|
|
81
82
|
Napi::TypeError::New(env, "Expected 'check' to be an integer").ThrowAsJavaScriptException();
|
|
82
83
|
return;
|
|
83
84
|
}
|
|
84
|
-
|
|
85
85
|
lzma_check check = static_cast<lzma_check>(optsCheck.ToNumber().Uint32Value());
|
|
86
86
|
|
|
87
87
|
Napi::Value optsPreset = opts.Get("preset");
|
|
88
|
-
if (!optsPreset.IsNumber())
|
|
88
|
+
if (!optsPreset.IsNumber())
|
|
89
|
+
{
|
|
89
90
|
Napi::TypeError::New(env, "Expected 'preset' to be an integer").ThrowAsJavaScriptException();
|
|
90
91
|
return;
|
|
91
92
|
}
|
|
92
|
-
|
|
93
93
|
uint32_t preset = optsPreset.ToNumber().Uint32Value();
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
if (!
|
|
97
|
-
|
|
95
|
+
// Initialize filters
|
|
96
|
+
if (!InitializeFilters(opts, preset))
|
|
97
|
+
{
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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();
|
|
109
113
|
return;
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
Napi::Error::New(env, "Unsupported preset, possibly a bug").ThrowAsJavaScriptException();
|
|
116
|
+
if (!success)
|
|
117
|
+
{
|
|
115
118
|
return;
|
|
116
119
|
}
|
|
117
120
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
for(uint32_t i = 0; i < filters_len; ++i) {
|
|
122
|
-
Napi::Value filter = filters_handle.Get(i);
|
|
123
|
-
if (!filter.IsNumber()) {
|
|
124
|
-
Napi::Error::New(env, "Filter must be an integer").ThrowAsJavaScriptException();
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
121
|
+
// Register external memory
|
|
122
|
+
Napi::MemoryManagement::AdjustExternalMemory(env, sizeof(LZMA));
|
|
123
|
+
}
|
|
127
124
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
|
|
139
|
+
// Smart pointer will automatically cleanup filter array
|
|
140
|
+
// filters.reset() is called automatically
|
|
138
141
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
ret = lzma_stream_decoder(&this->_stream, UINT64_MAX, check);
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
case STREAM_ENCODE: {
|
|
146
|
-
Napi::Value optsThreads = opts.Get("threads");
|
|
147
|
-
if (!optsThreads.IsNumber()) {
|
|
148
|
-
Napi::Error::New(env, "Threads must be an integer");
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
142
|
+
// Ensure LZMA stream is properly cleaned up
|
|
143
|
+
lzma_end(&_stream);
|
|
144
|
+
}
|
|
151
145
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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();
|
|
156
169
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
};
|
|
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
|
+
}
|
|
166
178
|
|
|
167
|
-
|
|
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;
|
|
168
184
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
#else
|
|
175
|
-
ret = lzma_stream_encoder(&this->_stream, filters, check);
|
|
176
|
-
#endif
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
default:
|
|
180
|
-
ret = LZMA_OPTIONS_ERROR;
|
|
185
|
+
// Execute based on mode with minimal branching
|
|
186
|
+
if constexpr (async)
|
|
187
|
+
{
|
|
188
|
+
return StartAsyncWork(info);
|
|
181
189
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return;
|
|
190
|
+
else
|
|
191
|
+
{
|
|
192
|
+
return ExecuteSyncWork(info);
|
|
186
193
|
}
|
|
194
|
+
}
|
|
187
195
|
|
|
188
|
-
|
|
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);
|
|
189
201
|
}
|
|
190
202
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
*
|
|
194
|
-
* Function prototype is (sync):
|
|
195
|
-
* .codeSync flushFlag, input_buffer, input_offset, output_buffer, output_offset, callback
|
|
196
|
-
* Function prototype is (async):
|
|
197
|
-
* .code flushFlag, input_buffer, input_offset, availInBefore, output_buffer, output_offset, callback
|
|
198
|
-
*
|
|
199
|
-
* Where:
|
|
200
|
-
* flushFlag: type: Uint32
|
|
201
|
-
* input_buffer: type: Buffer
|
|
202
|
-
* input_offset: type: Uint32
|
|
203
|
-
* availInBefore: type: Uint32
|
|
204
|
-
* output_buffer: type: Buffer
|
|
205
|
-
* output_offset: type: Uint32
|
|
206
|
-
* callback: type: Function
|
|
207
|
-
*/
|
|
208
|
-
template<bool async>
|
|
209
|
-
Napi::Value LZMA::Code(const Napi::CallbackInfo &info) {
|
|
203
|
+
Napi::Value LZMA::AfterSync(const Napi::CallbackInfo &info, LZMA *obj)
|
|
204
|
+
{
|
|
210
205
|
Napi::Env env = info.Env();
|
|
206
|
+
return obj->After(env);
|
|
207
|
+
}
|
|
211
208
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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;
|
|
221
225
|
}
|
|
222
226
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
227
|
+
// Validate flush flag
|
|
228
|
+
if (!info[0].IsNumber())
|
|
229
|
+
{
|
|
230
|
+
Napi::Error::New(env, "flushFlag must be an integer").ThrowAsJavaScriptException();
|
|
231
|
+
return false;
|
|
226
232
|
}
|
|
227
233
|
this->_action = static_cast<lzma_action>(info[0].ToNumber().Uint32Value());
|
|
228
234
|
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
} else {
|
|
243
|
-
// if (!node::Buffer::HasInstance(info[1])) {
|
|
244
|
-
if (!info[1].IsBuffer()) {
|
|
245
|
-
Napi::TypeError::New(env, "BUG?: LZMA::Code 'input_buffer' argument must be a Buffer").ThrowAsJavaScriptException();
|
|
246
|
-
return env.Undefined();
|
|
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;
|
|
247
248
|
}
|
|
248
249
|
|
|
249
250
|
uint8_t *in_buf = info[1].As<Napi::Buffer<uint8_t>>().Data();
|
|
250
|
-
in_off = info[2].ToNumber().Uint32Value();
|
|
251
|
-
in_len = info[3].ToNumber().Uint32Value();
|
|
251
|
+
ctx.in_off = info[2].ToNumber().Uint32Value();
|
|
252
|
+
ctx.in_len = info[3].ToNumber().Uint32Value();
|
|
252
253
|
size_t in_max = info[1].As<Napi::Buffer<uint8_t>>().Length();
|
|
253
254
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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;
|
|
257
266
|
}
|
|
258
|
-
in = in_buf + in_off;
|
|
267
|
+
ctx.in = in_buf + ctx.in_off;
|
|
259
268
|
}
|
|
260
269
|
|
|
261
|
-
|
|
262
|
-
if(
|
|
263
|
-
|
|
264
|
-
|
|
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;
|
|
265
275
|
}
|
|
266
276
|
|
|
267
277
|
uint8_t *out_buf = info[4].As<Napi::Buffer<uint8_t>>().Data();
|
|
268
|
-
|
|
269
|
-
out_len = info[4].As<Napi::Buffer<uint8_t>>().Length() - out_off;
|
|
270
|
-
out = out_buf + out_off;
|
|
278
|
+
size_t out_max = info[4].As<Napi::Buffer<uint8_t>>().Length();
|
|
271
279
|
|
|
272
|
-
//
|
|
273
|
-
if(
|
|
274
|
-
|
|
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;
|
|
275
285
|
}
|
|
276
286
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
+
}
|
|
289
299
|
}
|
|
290
300
|
|
|
291
|
-
|
|
292
|
-
// napi_create_async_work(uv_default_loop(), &(this->_req), LZMA::Process, LZMA::After);
|
|
293
|
-
return env.Undefined();
|
|
301
|
+
return true;
|
|
294
302
|
}
|
|
295
303
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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;
|
|
300
411
|
}
|
|
301
412
|
|
|
302
|
-
|
|
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
|
+
}
|
|
303
424
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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"
|
|
307
429
|
|
|
308
|
-
|
|
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
|
+
};
|
|
309
439
|
|
|
310
|
-
|
|
440
|
+
#pragma GCC diagnostic pop
|
|
311
441
|
|
|
312
|
-
|
|
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
|
|
313
454
|
|
|
314
|
-
if(
|
|
315
|
-
|
|
455
|
+
if (ret != LZMA_OK)
|
|
456
|
+
{
|
|
457
|
+
Napi::Error::New(env, "LZMA encoder failure, returned " + std::to_string(ret)).ThrowAsJavaScriptException();
|
|
458
|
+
return false;
|
|
316
459
|
}
|
|
460
|
+
|
|
461
|
+
return true;
|
|
317
462
|
}
|
|
318
463
|
|
|
319
|
-
|
|
320
|
-
|
|
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);
|
|
321
500
|
|
|
322
|
-
Napi::Number ret_code = Napi::Number::New(env, obj->_ret);
|
|
323
|
-
Napi::Number avail_in = Napi::Number::New(env, obj->_stream.avail_in);
|
|
324
|
-
Napi::Number avail_out = Napi::Number::New(env, obj->_stream.avail_out);
|
|
325
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;
|
|
326
505
|
|
|
327
|
-
|
|
328
|
-
result[i++] = ret_code;
|
|
329
|
-
result[i++] = avail_in;
|
|
330
|
-
result[i++] = avail_out;
|
|
506
|
+
AfterCommon(env);
|
|
331
507
|
|
|
332
|
-
|
|
508
|
+
return result;
|
|
509
|
+
}
|
|
333
510
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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});
|
|
338
520
|
|
|
339
|
-
|
|
521
|
+
AfterCommon(env);
|
|
340
522
|
}
|