Skip to content

Commit

Permalink
Multi-threading encoding support
Browse files Browse the repository at this point in the history
  • Loading branch information
addaleax committed Jun 29, 2016
1 parent f66c2bf commit 6211ea2
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ coverage/
npm-debug.log
core
README.md.xz
.nyc_output
1 change: 1 addition & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"src/filter-array.cpp",
"src/lzma-stream.cpp",
"src/module.cpp",
"src/mt-options.cpp",
"src/index-parser.cpp"
],
"include_dirs" : [
Expand Down
26 changes: 24 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,33 @@ Stream.prototype.rawDecoder = function(options) {
};

Stream.prototype.easyEncoder = function(options) {
return this.easyEncoder_(options.preset || exports.PRESET_DEFAULT, options.check || exports.CHECK_CRC32);
var preset = options.preset || exports.PRESET_DEFAULT;
var check = options.check || exports.CHECK_CRC32;

if (typeof options.threads !== 'undefined' && options.threads !== null) {
return this.mtEncoder_(extend({
preset: preset,
filters: null,
check: check
}, options));
} else {
return this.easyEncoder_(preset, check);
}
};

Stream.prototype.streamEncoder = function(options) {
return this.streamEncoder_(options.filters || [], options.check || exports.CHECK_CRC32);
var filters = options.filters || [];
var check = options.check || exports.CHECK_CRC32;

if (typeof options.threads !== 'undefined' && options.threads !== null) {
return this.mtEncoder_(extend({
preset: null,
filters: filters,
check: check
}, options));
} else {
return this.streamEncoder_(filters, check);
}
};

Stream.prototype.streamDecoder = function(options) {
Expand Down
24 changes: 23 additions & 1 deletion src/liblzma-node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ namespace lzma {
*/
class FilterArray {
public:
FilterArray() { finish(); }
FilterArray() : ok_(false) { finish(); }
explicit FilterArray(Local<Array> arr);

lzma_filter* array() { return filters.data(); }
Expand All @@ -191,6 +191,27 @@ namespace lzma {
std::list<options> optbuf;
};

/**
* Wrapper for lzma_mt (multi-threading options).
*/
class MTOptions {
public:
MTOptions();
explicit MTOptions(Local<Object> opt);
~MTOptions();

lzma_mt* opts() { return &opts_; }
const lzma_mt* opts() const { return &opts_; }
bool ok() const { return ok_; }
private:
MTOptions(const MTOptions&);
MTOptions& operator=(const MTOptions&);

FilterArray* filters_;
lzma_mt opts_;
bool ok_;
};

/**
* Node.js object wrap for lzma_stream wrapper. Corresponds to exports.Stream
*/
Expand Down Expand Up @@ -241,6 +262,7 @@ namespace lzma {
static NAN_METHOD(EasyEncoder);
static NAN_METHOD(StreamEncoder);
static NAN_METHOD(AloneEncoder);
static NAN_METHOD(MTEncoder);
static NAN_METHOD(StreamDecoder);
static NAN_METHOD(AutoDecoder);
static NAN_METHOD(AloneDecoder);
Expand Down
16 changes: 16 additions & 0 deletions src/lzma-stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ void LZMAStream::Init(Local<Object> exports) {
Nan::SetPrototypeMethod(tpl, "easyEncoder_", EasyEncoder);
Nan::SetPrototypeMethod(tpl, "streamEncoder_", StreamEncoder);
Nan::SetPrototypeMethod(tpl, "aloneEncoder", AloneEncoder);
Nan::SetPrototypeMethod(tpl, "mtEncoder_", MTEncoder);
Nan::SetPrototypeMethod(tpl, "streamDecoder_", StreamDecoder);
Nan::SetPrototypeMethod(tpl, "autoDecoder_", AutoDecoder);
Nan::SetPrototypeMethod(tpl, "aloneDecoder_", AloneDecoder);
Expand Down Expand Up @@ -449,10 +450,25 @@ NAN_METHOD(LZMAStream::StreamEncoder) {

const FilterArray filters(Local<Array>::Cast(info[0]));
Local<Integer> check = Local<Integer>::Cast(info[1]);

if (!filters.ok())
return;

info.GetReturnValue().Set(lzmaRet(lzma_stream_encoder(&self->_, filters.array(), (lzma_check) check->Value())));
}

NAN_METHOD(LZMAStream::MTEncoder) {
LZMA_FETCH_SELF();
LZMA_ASYNC_LOCK(self);

const MTOptions mt(Local<Object>::Cast(info[0]));

if (!mt.ok())
return;

info.GetReturnValue().Set(lzmaRet(lzma_stream_encoder_mt(&self->_, mt.opts())));
}

NAN_METHOD(LZMAStream::AloneEncoder) {
LZMA_FETCH_SELF();
LZMA_ASYNC_LOCK(self);
Expand Down
40 changes: 40 additions & 0 deletions src/mt-options.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "liblzma-node.hpp"

namespace lzma {

MTOptions::MTOptions() : filters_(NULL), ok_(false) { }

MTOptions::~MTOptions() {
delete filters_;
}

MTOptions::MTOptions(Local<Object> opt) : filters_(NULL), ok_(true) {
opts_.flags = 0;
opts_.filters = NULL;

opts_.block_size = Nan::Get(opt, NewString("block_size")).ToLocalChecked()->IntegerValue();
opts_.timeout = Nan::To<uint32_t>(Nan::Get(opt, NewString("timeout")).ToLocalChecked())
.FromMaybe(0);
opts_.preset = Nan::To<uint32_t>(Nan::Get(opt, NewString("preset")).ToLocalChecked())
.FromMaybe(LZMA_PRESET_DEFAULT);
opts_.check = (lzma_check)Nan::To<int32_t>(Nan::Get(opt, NewString("check")).ToLocalChecked())
.FromMaybe((int)LZMA_CHECK_CRC64);
opts_.threads = Nan::To<uint32_t>(Nan::Get(opt, NewString("threads")).ToLocalChecked())
.FromMaybe(0);

if (opts_.threads == 0) {
opts_.threads = lzma_cputhreads();
}

Local<Value> filters = Nan::Get(opt, NewString("filters")).ToLocalChecked();
if (filters->IsArray()) {
filters_ = new FilterArray(Local<Array>::Cast(filters));
if (filters_->ok()) {
opts_.filters = filters_->array();
} else {
ok_ = false;
}
}
}

}
47 changes: 47 additions & 0 deletions test/stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,24 @@ describe('LZMAStream', function() {
});
});

it('should correctly encode the empty string in async MT mode', function(done) {
var enc = lzma.createStream('easyEncoder', { threads: 2 });
var dec = lzma.createStream('autoDecoder');
encodeAndDecode(enc, dec, done, bl(''));
});

it('should correctly encode the empty string in async MT mode with default threading', function(done) {
var enc = lzma.createStream('easyEncoder', { threads: 0 });
var dec = lzma.createStream('autoDecoder');
encodeAndDecode(enc, dec, done, bl(''));
});

it('should correctly encode the empty string in sync MT mode', function(done) {
var enc = lzma.createStream('easyEncoder', { threads: 2, synchronous: true });
var dec = lzma.createStream('autoDecoder');
encodeAndDecode(enc, dec, done, bl(''));
});

it('should correctly encode the empty string in async mode', function(done) {
var enc = lzma.createStream('easyEncoder');
var dec = lzma.createStream('autoDecoder');
Expand Down Expand Up @@ -258,6 +276,35 @@ describe('LZMAStream', function() {
encodeAndDecode(enc, dec, done, x86BinaryData);
});

it('should be undone by autoDecoder in async mode using the x86 filter in MT mode', function(done) {
var enc = lzma.createStream('streamEncoder', {
filters: [
{ id: lzma.FILTER_X86 },
{ id: lzma.FILTER_LZMA2 }
],
check: lzma.CHECK_SHA256,
threads: 2
});
var dec = lzma.createStream('autoDecoder');

encodeAndDecode(enc, dec, done, x86BinaryData);
});

it('should be undone by autoDecoder in sync mode using the x86 filter in MT mode', function(done) {
var enc = lzma.createStream('streamEncoder', {
filters: [
{ id: lzma.FILTER_X86 },
{ id: lzma.FILTER_LZMA2 }
],
check: lzma.CHECK_SHA256,
synchronous: true,
threads: 2
});
var dec = lzma.createStream('autoDecoder', {synchronous: true});

encodeAndDecode(enc, dec, done, x86BinaryData);
});

it('should be undone by streamDecoder in async mode using the delta filter', function(done) {
var enc = lzma.createStream('streamEncoder', {
filters: [
Expand Down

0 comments on commit 6211ea2

Please sign in to comment.