Skip to content

Commit 9b37b83

Browse files
seishunindutny
authored andcommitted
zlib: add sync versions for convenience methods
1 parent 49c2372 commit 9b37b83

File tree

4 files changed

+152
-39
lines changed

4 files changed

+152
-39
lines changed

doc/api/zlib.markdown

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -195,34 +195,36 @@ the header.
195195
<!--type=misc-->
196196

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

201-
## zlib.deflate(buf, [options], callback)
203+
## zlib.deflate(buf, [options], [callback])
202204

203205
Compress a string with Deflate.
204206

205-
## zlib.deflateRaw(buf, [options], callback)
207+
## zlib.deflateRaw(buf, [options], [callback])
206208

207209
Compress a string with DeflateRaw.
208210

209-
## zlib.gzip(buf, [options], callback)
211+
## zlib.gzip(buf, [options], [callback])
210212

211213
Compress a string with Gzip.
212214

213-
## zlib.gunzip(buf, [options], callback)
215+
## zlib.gunzip(buf, [options], [callback])
214216

215217
Decompress a raw Buffer with Gunzip.
216218

217-
## zlib.inflate(buf, [options], callback)
219+
## zlib.inflate(buf, [options], [callback])
218220

219221
Decompress a raw Buffer with Inflate.
220222

221-
## zlib.inflateRaw(buf, [options], callback)
223+
## zlib.inflateRaw(buf, [options], [callback])
222224

223225
Decompress a raw Buffer with InflateRaw.
224226

225-
## zlib.unzip(buf, [options], callback)
227+
## zlib.unzip(buf, [options], [callback])
226228

227229
Decompress a raw Buffer with Unzip.
228230

lib/zlib.js

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,58 +112,62 @@ exports.deflate = function(buffer, opts, callback) {
112112
callback = opts;
113113
opts = {};
114114
}
115-
zlibBuffer(new Deflate(opts), buffer, callback);
115+
return zlibBuffer(new Deflate(opts), buffer, callback);
116116
};
117117

118118
exports.gzip = function(buffer, opts, callback) {
119119
if (util.isFunction(opts)) {
120120
callback = opts;
121121
opts = {};
122122
}
123-
zlibBuffer(new Gzip(opts), buffer, callback);
123+
return zlibBuffer(new Gzip(opts), buffer, callback);
124124
};
125125

126126
exports.deflateRaw = function(buffer, opts, callback) {
127127
if (util.isFunction(opts)) {
128128
callback = opts;
129129
opts = {};
130130
}
131-
zlibBuffer(new DeflateRaw(opts), buffer, callback);
131+
return zlibBuffer(new DeflateRaw(opts), buffer, callback);
132132
};
133133

134134
exports.unzip = function(buffer, opts, callback) {
135135
if (util.isFunction(opts)) {
136136
callback = opts;
137137
opts = {};
138138
}
139-
zlibBuffer(new Unzip(opts), buffer, callback);
139+
return zlibBuffer(new Unzip(opts), buffer, callback);
140140
};
141141

142142
exports.inflate = function(buffer, opts, callback) {
143143
if (util.isFunction(opts)) {
144144
callback = opts;
145145
opts = {};
146146
}
147-
zlibBuffer(new Inflate(opts), buffer, callback);
147+
return zlibBuffer(new Inflate(opts), buffer, callback);
148148
};
149149

150150
exports.gunzip = function(buffer, opts, callback) {
151151
if (util.isFunction(opts)) {
152152
callback = opts;
153153
opts = {};
154154
}
155-
zlibBuffer(new Gunzip(opts), buffer, callback);
155+
return zlibBuffer(new Gunzip(opts), buffer, callback);
156156
};
157157

158158
exports.inflateRaw = function(buffer, opts, callback) {
159159
if (util.isFunction(opts)) {
160160
callback = opts;
161161
opts = {};
162162
}
163-
zlibBuffer(new InflateRaw(opts), buffer, callback);
163+
return zlibBuffer(new InflateRaw(opts), buffer, callback);
164164
};
165165

166166
function zlibBuffer(engine, buffer, callback) {
167+
if (!util.isFunction(callback)) {
168+
return zlibBufferSync(engine, buffer, callback);
169+
}
170+
167171
var buffers = [];
168172
var nread = 0;
169173

@@ -196,6 +200,16 @@ function zlibBuffer(engine, buffer, callback) {
196200
}
197201
}
198202

203+
function zlibBufferSync(engine, buffer, callback) {
204+
if (util.isString(buffer))
205+
buffer = new Buffer(buffer);
206+
if (!util.isBuffer(buffer))
207+
throw new TypeError('Not a string or buffer');
208+
209+
var flushFlag = binding.Z_FINISH;
210+
211+
return engine._processChunk(buffer, flushFlag);
212+
}
199213

200214
// generic zlib
201215
// minimal 2-byte header
@@ -453,10 +467,48 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
453467
}
454468
}
455469

470+
var self = this;
471+
this._processChunk(chunk, flushFlag, cb);
472+
};
473+
474+
Zlib.prototype._processChunk = function(chunk, flushFlag, cb) {
456475
var availInBefore = chunk && chunk.length;
457476
var availOutBefore = this._chunkSize - this._offset;
458477
var inOff = 0;
459478

479+
var self = this;
480+
481+
var async = util.isFunction(cb);
482+
483+
if (!async) {
484+
var buffers = [];
485+
var nread = 0;
486+
487+
var error;
488+
this.on('error', function(er) {
489+
error = er;
490+
});
491+
492+
do {
493+
var res = this._binding.writeSync(flushFlag,
494+
chunk, // in
495+
inOff, // in_off
496+
availInBefore, // in_len
497+
this._buffer, // out
498+
this._offset, //out_off
499+
availOutBefore); // out_len
500+
} while (!this._hadError && callback(res[0], res[1]));
501+
502+
if (this._hadError) {
503+
throw error;
504+
}
505+
506+
var buf = Buffer.concat(buffers, nread);
507+
this.close();
508+
509+
return buf;
510+
}
511+
460512
var req = this._binding.write(flushFlag,
461513
chunk, // in
462514
inOff, // in_off
@@ -468,7 +520,6 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
468520
req.buffer = chunk;
469521
req.callback = callback;
470522

471-
var self = this;
472523
function callback(availInAfter, availOutAfter) {
473524
if (self._hadError)
474525
return;
@@ -480,7 +531,12 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
480531
var out = self._buffer.slice(self._offset, self._offset + have);
481532
self._offset += have;
482533
// serve some output to the consumer.
483-
self.push(out);
534+
if (async) {
535+
self.push(out);
536+
} else {
537+
buffers.push(out);
538+
nread += out.length;
539+
}
484540
}
485541

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

557+
if (!async)
558+
return true;
559+
501560
var newReq = self._binding.write(flushFlag,
502561
chunk,
503562
inOff,
@@ -510,6 +569,9 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
510569
return;
511570
}
512571

572+
if (!async)
573+
return false;
574+
513575
// finished with the chunk.
514576
cb();
515577
}

src/node_zlib.cc

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
namespace node {
4141

42+
using v8::Array;
4243
using v8::Context;
4344
using v8::FunctionCallbackInfo;
4445
using v8::FunctionTemplate;
@@ -125,6 +126,7 @@ class ZCtx : public AsyncWrap {
125126

126127

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

195+
if (!async) {
196+
// sync version
197+
Process(work_req);
198+
if (CheckError(ctx))
199+
AfterSync(ctx, args);
200+
return;
201+
}
202+
203+
// async version
193204
uv_queue_work(ctx->env()->event_loop(),
194205
work_req,
195206
ZCtx::Process,
@@ -199,6 +210,21 @@ class ZCtx : public AsyncWrap {
199210
}
200211

201212

213+
static void AfterSync(ZCtx* ctx, const FunctionCallbackInfo<Value>& args) {
214+
Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out, node_isolate);
215+
Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in, node_isolate);
216+
217+
ctx->write_in_progress_ = false;
218+
219+
Local<Array> result = Array::New(2);
220+
result->Set(0, avail_in);
221+
result->Set(1, avail_out);
222+
args.GetReturnValue().Set(result);
223+
224+
ctx->Unref();
225+
}
226+
227+
202228
// thread pool!
203229
// This function may be called multiple times on the uv_work pool
204230
// for a single write() call, until all of the input bytes have
@@ -249,6 +275,31 @@ class ZCtx : public AsyncWrap {
249275
// or shift the queue and call Process.
250276
}
251277

278+
279+
static bool CheckError(ZCtx* ctx) {
280+
// Acceptable error states depend on the type of zlib stream.
281+
switch (ctx->err_) {
282+
case Z_OK:
283+
case Z_STREAM_END:
284+
case Z_BUF_ERROR:
285+
// normal statuses, not fatal
286+
break;
287+
case Z_NEED_DICT:
288+
if (ctx->dictionary_ == NULL)
289+
ZCtx::Error(ctx, "Missing dictionary");
290+
else
291+
ZCtx::Error(ctx, "Bad dictionary");
292+
return false;
293+
default:
294+
// something else.
295+
ZCtx::Error(ctx, "Zlib error");
296+
return false;
297+
}
298+
299+
return true;
300+
}
301+
302+
252303
// v8 land!
253304
static void After(uv_work_t* work_req, int status) {
254305
assert(status == 0);
@@ -259,25 +310,8 @@ class ZCtx : public AsyncWrap {
259310
HandleScope handle_scope(env->isolate());
260311
Context::Scope context_scope(env->context());
261312

262-
// Acceptable error states depend on the type of zlib stream.
263-
switch (ctx->err_) {
264-
case Z_OK:
265-
case Z_STREAM_END:
266-
case Z_BUF_ERROR:
267-
// normal statuses, not fatal
268-
break;
269-
case Z_NEED_DICT:
270-
if (ctx->dictionary_ == NULL) {
271-
ZCtx::Error(ctx, "Missing dictionary");
272-
} else {
273-
ZCtx::Error(ctx, "Bad dictionary");
274-
}
275-
return;
276-
default:
277-
// something else.
278-
ZCtx::Error(ctx, "Zlib error");
279-
return;
280-
}
313+
if (!CheckError(ctx))
314+
return;
281315

282316
Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out, node_isolate);
283317
Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in, node_isolate);
@@ -556,7 +590,8 @@ void InitZlib(Handle<Object> target,
556590

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

559-
NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx::Write);
593+
NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx::Write<true>);
594+
NODE_SET_PROTOTYPE_METHOD(z, "writeSync", ZCtx::Write<false>);
560595
NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx::Init);
561596
NODE_SET_PROTOTYPE_METHOD(z, "close", ZCtx::Close);
562597
NODE_SET_PROTOTYPE_METHOD(z, "params", ZCtx::Params);

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,22 @@ var opts = {
5858
});
5959
});
6060

61+
var result = zlib[method[0]](expect, opts);
62+
result = zlib[method[1]](result, opts);
63+
assert.equal(result, expect,
64+
'Should get original string after ' +
65+
method[0] + '/' + method[1] + ' with options.');
66+
hadRun++;
67+
68+
result = zlib[method[0]](expect);
69+
result = zlib[method[1]](result);
70+
assert.equal(result, expect,
71+
'Should get original string after ' +
72+
method[0] + '/' + method[1] + ' without options.');
73+
hadRun++;
74+
6175
});
6276

6377
process.on('exit', function() {
64-
assert.equal(hadRun, 8, 'expect 8 compressions');
78+
assert.equal(hadRun, 16, 'expect 16 compressions');
6579
});

0 commit comments

Comments
 (0)