Browse Source

zlib: add sync versions for convenience methods

v0.11.12-release
Nikolai Vavilov 11 years ago
committed by Fedor Indutny
parent
commit
9b37b83a20
  1. 20
      doc/api/zlib.markdown
  2. 80
      lib/zlib.js
  3. 75
      src/node_zlib.cc
  4. 16
      test/simple/test-zlib-convenience-methods.js

20
doc/api/zlib.markdown

@ -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.

80
lib/zlib.js

@ -112,7 +112,7 @@ 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) {
@ -120,7 +120,7 @@ exports.gzip = function(buffer, opts, callback) {
callback = opts;
opts = {};
}
zlibBuffer(new Gzip(opts), buffer, callback);
return zlibBuffer(new Gzip(opts), buffer, callback);
};
exports.deflateRaw = function(buffer, opts, callback) {
@ -128,7 +128,7 @@ exports.deflateRaw = function(buffer, opts, callback) {
callback = opts;
opts = {};
}
zlibBuffer(new DeflateRaw(opts), buffer, callback);
return zlibBuffer(new DeflateRaw(opts), buffer, callback);
};
exports.unzip = function(buffer, opts, callback) {
@ -136,7 +136,7 @@ exports.unzip = function(buffer, opts, callback) {
callback = opts;
opts = {};
}
zlibBuffer(new Unzip(opts), buffer, callback);
return zlibBuffer(new Unzip(opts), buffer, callback);
};
exports.inflate = function(buffer, opts, callback) {
@ -144,7 +144,7 @@ exports.inflate = function(buffer, opts, callback) {
callback = opts;
opts = {};
}
zlibBuffer(new Inflate(opts), buffer, callback);
return zlibBuffer(new Inflate(opts), buffer, callback);
};
exports.gunzip = function(buffer, opts, callback) {
@ -152,7 +152,7 @@ exports.gunzip = function(buffer, opts, callback) {
callback = opts;
opts = {};
}
zlibBuffer(new Gunzip(opts), buffer, callback);
return zlibBuffer(new Gunzip(opts), buffer, callback);
};
exports.inflateRaw = function(buffer, opts, callback) {
@ -160,10 +160,14 @@ exports.inflateRaw = function(buffer, opts, callback) {
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;
@ -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
@ -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
@ -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;
@ -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) {
self.push(out);
} else {
buffers.push(out);
nread += out.length;
}
}
// exhausted the output buffer, or used all the input create a new one.
@ -498,6 +554,9 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
inOff += (availInBefore - availInAfter);
availInBefore = availInAfter;
if (!async)
return true;
var newReq = self._binding.write(flushFlag,
chunk,
inOff,
@ -510,6 +569,9 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
return;
}
if (!async)
return false;
// finished with the chunk.
cb();
}

75
src/node_zlib.cc

@ -39,6 +39,7 @@
namespace node {
using v8::Array;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
@ -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);
@ -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) {
// 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,
@ -199,6 +210,21 @@ class ZCtx : public AsyncWrap {
}
static void AfterSync(ZCtx* ctx, const FunctionCallbackInfo<Value>& args) {
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
// for a single write() call, until all of the input bytes have
@ -249,6 +275,31 @@ class ZCtx : public AsyncWrap {
// or shift the queue and call Process.
}
static bool CheckError(ZCtx* ctx) {
// 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;
}
// v8 land!
static void After(uv_work_t* work_req, int status) {
assert(status == 0);
@ -259,25 +310,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);
@ -556,7 +590,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);

16
test/simple/test-zlib-convenience-methods.js

@ -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');
});

Loading…
Cancel
Save