Skip to content
This repository was archived by the owner on Apr 22, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions doc/api/zlib.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -195,34 +195,36 @@ the header.
<!--type=misc-->

All of these take a string or buffer as the first argument, an optional second
argument to supply options to the zlib classes and will call the supplied
callback with `callback(error, result)`.
argument to supply options to the zlib classes and an optional callback. If a
callback is supplied, they will call it asynchronously with
`callback(error, result)`, otherwise they will return the result or throw the
error synchronously.

## zlib.deflate(buf, [options], callback)
## zlib.deflate(buf, [options], [callback])

Compress a string with Deflate.

## zlib.deflateRaw(buf, [options], callback)
## zlib.deflateRaw(buf, [options], [callback])

Compress a string with DeflateRaw.

## zlib.gzip(buf, [options], callback)
## zlib.gzip(buf, [options], [callback])

Compress a string with Gzip.

## zlib.gunzip(buf, [options], callback)
## zlib.gunzip(buf, [options], [callback])

Decompress a raw Buffer with Gunzip.

## zlib.inflate(buf, [options], callback)
## zlib.inflate(buf, [options], [callback])

Decompress a raw Buffer with Inflate.

## zlib.inflateRaw(buf, [options], callback)
## zlib.inflateRaw(buf, [options], [callback])

Decompress a raw Buffer with InflateRaw.

## zlib.unzip(buf, [options], callback)
## zlib.unzip(buf, [options], [callback])

Decompress a raw Buffer with Unzip.

Expand Down
80 changes: 71 additions & 9 deletions lib/zlib.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,58 +112,62 @@ exports.deflate = function(buffer, opts, callback) {
callback = opts;
opts = {};
}
zlibBuffer(new Deflate(opts), buffer, callback);
return zlibBuffer(new Deflate(opts), buffer, callback);
};

exports.gzip = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new Gzip(opts), buffer, callback);
return zlibBuffer(new Gzip(opts), buffer, callback);
};

exports.deflateRaw = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new DeflateRaw(opts), buffer, callback);
return zlibBuffer(new DeflateRaw(opts), buffer, callback);
};

exports.unzip = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new Unzip(opts), buffer, callback);
return zlibBuffer(new Unzip(opts), buffer, callback);
};

exports.inflate = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new Inflate(opts), buffer, callback);
return zlibBuffer(new Inflate(opts), buffer, callback);
};

exports.gunzip = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new Gunzip(opts), buffer, callback);
return zlibBuffer(new Gunzip(opts), buffer, callback);
};

exports.inflateRaw = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new InflateRaw(opts), buffer, callback);
return zlibBuffer(new InflateRaw(opts), buffer, callback);
};

function zlibBuffer(engine, buffer, callback) {
if (!util.isFunction(callback)) {
return zlibBufferSync(engine, buffer, callback);
}

var buffers = [];
var nread = 0;

Expand Down Expand Up @@ -196,6 +200,16 @@ function zlibBuffer(engine, buffer, callback) {
}
}

function zlibBufferSync(engine, buffer, callback) {
if (util.isString(buffer))
buffer = new Buffer(buffer);
if (!util.isBuffer(buffer))
throw new TypeError('Not a string or buffer');

var flushFlag = binding.Z_FINISH;

return engine._processChunk(buffer, flushFlag);
}

// generic zlib
// minimal 2-byte header
Expand Down Expand Up @@ -453,10 +467,48 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
}
}

var self = this;
this._processChunk(chunk, flushFlag, cb);
};

Zlib.prototype._processChunk = function(chunk, flushFlag, cb) {
var availInBefore = chunk && chunk.length;
var availOutBefore = this._chunkSize - this._offset;
var inOff = 0;

var self = this;

var async = util.isFunction(cb);

if (!async) {
var buffers = [];
var nread = 0;

var error;
this.on('error', function(er) {
error = er;
});

do {
var res = this._binding.writeSync(flushFlag,
chunk, // in
inOff, // in_off
availInBefore, // in_len
this._buffer, // out
this._offset, //out_off
availOutBefore); // out_len
} while (!this._hadError && callback(res[0], res[1]));

if (this._hadError) {
throw error;
}

var buf = Buffer.concat(buffers, nread);
this.close();

return buf;
}

var req = this._binding.write(flushFlag,
chunk, // in
inOff, // in_off
Expand All @@ -468,7 +520,6 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
req.buffer = chunk;
req.callback = callback;

var self = this;
function callback(availInAfter, availOutAfter) {
if (self._hadError)
return;
Expand All @@ -480,7 +531,12 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
var out = self._buffer.slice(self._offset, self._offset + have);
self._offset += have;
// serve some output to the consumer.
self.push(out);
if (async)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add braces here, when else has braces - if should have them too.

self.push(out);
else {
buffers.push(out);
nread += out.length;
}
}

// exhausted the output buffer, or used all the input create a new one.
Expand All @@ -498,6 +554,9 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
inOff += (availInBefore - availInAfter);
availInBefore = availInAfter;

if (!async)
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very clever


var newReq = self._binding.write(flushFlag,
chunk,
inOff,
Expand All @@ -510,6 +569,9 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
return;
}

if (!async)
return false;

// finished with the chunk.
cb();
}
Expand Down
72 changes: 52 additions & 20 deletions src/node_zlib.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

namespace node {

using v8::Array;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
Expand Down Expand Up @@ -125,6 +126,7 @@ class ZCtx : public AsyncWrap {


// write(flush, in, in_off, in_len, out, out_off, out_len)
template <bool async>
static void Write(const FunctionCallbackInfo<Value>& args) {
HandleScope scope(node_isolate);
assert(args.Length() == 7);
Expand Down Expand Up @@ -190,6 +192,15 @@ class ZCtx : public AsyncWrap {
// set this so that later on, I can easily tell how much was written.
ctx->chunk_size_ = out_len;

if (!async) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split into another method, please.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean the if clause's body.

// sync version
Process(work_req);
if (CheckError(ctx))
AfterSync(ctx, args);
return;
}

// async version
uv_queue_work(ctx->env()->event_loop(),
work_req,
ZCtx::Process,
Expand All @@ -198,6 +209,20 @@ class ZCtx : public AsyncWrap {
args.GetReturnValue().Set(ctx->object());
}

static void AfterSync(ZCtx* ctx, const FunctionCallbackInfo<Value>& args) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a newline before declaration.

Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out, node_isolate);
Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in, node_isolate);

ctx->write_in_progress_ = false;

Local<Array> result = Array::New(2);
result->Set(0, avail_in);
result->Set(1, avail_out);
args.GetReturnValue().Set(result);

ctx->Unref();
}


// thread pool!
// This function may be called multiple times on the uv_work pool
Expand Down Expand Up @@ -249,6 +274,29 @@ class ZCtx : public AsyncWrap {
// or shift the queue and call Process.
}

static bool CheckError(ZCtx* ctx) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

// Acceptable error states depend on the type of zlib stream.
switch (ctx->err_) {
case Z_OK:
case Z_STREAM_END:
case Z_BUF_ERROR:
// normal statuses, not fatal
break;
case Z_NEED_DICT:
if (ctx->dictionary_ == NULL)
ZCtx::Error(ctx, "Missing dictionary");
else
ZCtx::Error(ctx, "Bad dictionary");
return false;
default:
// something else.
ZCtx::Error(ctx, "Zlib error");
return false;
}

return true;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newline

// v8 land!
static void After(uv_work_t* work_req, int status) {
assert(status == 0);
Expand All @@ -259,25 +307,8 @@ class ZCtx : public AsyncWrap {
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());

// Acceptable error states depend on the type of zlib stream.
switch (ctx->err_) {
case Z_OK:
case Z_STREAM_END:
case Z_BUF_ERROR:
// normal statuses, not fatal
break;
case Z_NEED_DICT:
if (ctx->dictionary_ == NULL) {
ZCtx::Error(ctx, "Missing dictionary");
} else {
ZCtx::Error(ctx, "Bad dictionary");
}
return;
default:
// something else.
ZCtx::Error(ctx, "Zlib error");
return;
}
if (!CheckError(ctx))
return;

Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out, node_isolate);
Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in, node_isolate);
Expand Down Expand Up @@ -556,7 +587,8 @@ void InitZlib(Handle<Object> target,

z->InstanceTemplate()->SetInternalFieldCount(1);

NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx::Write);
NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx::Write<true>);
NODE_SET_PROTOTYPE_METHOD(z, "writeSync", ZCtx::Write<false>);
NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx::Init);
NODE_SET_PROTOTYPE_METHOD(z, "close", ZCtx::Close);
NODE_SET_PROTOTYPE_METHOD(z, "params", ZCtx::Params);
Expand Down
16 changes: 15 additions & 1 deletion test/simple/test-zlib-convenience-methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,22 @@ var opts = {
});
});

var result = zlib[method[0]](expect, opts);
result = zlib[method[1]](result, opts);
assert.equal(result, expect,
'Should get original string after ' +
method[0] + '/' + method[1] + ' with options.');
hadRun++;

result = zlib[method[0]](expect);
result = zlib[method[1]](result);
assert.equal(result, expect,
'Should get original string after ' +
method[0] + '/' + method[1] + ' without options.');
hadRun++;

});

process.on('exit', function() {
assert.equal(hadRun, 8, 'expect 8 compressions');
assert.equal(hadRun, 16, 'expect 16 compressions');
});