<!--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.
callback = opts;
opts = {};
}
- zlibBuffer(new Deflate(opts), buffer, callback);
+ return zlibBuffer(new Deflate(opts), buffer, callback);
};
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) {
callback = opts;
opts = {};
}
- zlibBuffer(new DeflateRaw(opts), buffer, callback);
+ return zlibBuffer(new DeflateRaw(opts), buffer, callback);
};
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) {
callback = opts;
opts = {};
}
- zlibBuffer(new Inflate(opts), buffer, callback);
+ return zlibBuffer(new Inflate(opts), buffer, callback);
};
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) {
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;
}
}
+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
}
}
+ 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
req.buffer = chunk;
req.callback = callback;
- var self = this;
function callback(availInAfter, availOutAfter) {
if (self._hadError)
return;
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.
inOff += (availInBefore - availInAfter);
availInBefore = availInAfter;
+ if (!async)
+ return true;
+
var newReq = self._binding.write(flushFlag,
chunk,
inOff,
return;
}
+ if (!async)
+ return false;
+
// finished with the chunk.
cb();
}
namespace node {
+using v8::Array;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
// 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);
// 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,
}
+ 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
// 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);
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);
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);
});
});
+ 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');
});