Skip to content

Commit 874377b

Browse files
committed
zlib: support Uint8Array in convenience methods
Also support Uint8Array as a `dictionary` option.
1 parent 61ebfa8 commit 874377b

6 files changed

+176
-87
lines changed

doc/api/zlib.md

+107-32
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ Compression strategy.
278278
<!-- YAML
279279
added: v0.11.1
280280
changes:
281+
- version: REPLACEME
282+
pr-url: https://github.com/nodejs/node/pull/12001
283+
description: The `dictionary` option can be an Uint8Array now.
281284
- version: v5.11.0
282285
pr-url: https://github.com/nodejs/node/pull/6069
283286
description: The `finishFlush` option is supported now.
@@ -290,14 +293,15 @@ Each class takes an `options` object. All options are optional.
290293
Note that some options are only relevant when compressing, and are
291294
ignored by the decompression classes.
292295

293-
* `flush` (default: `zlib.constants.Z_NO_FLUSH`)
294-
* `finishFlush` (default: `zlib.constants.Z_FINISH`)
295-
* `chunkSize` (default: 16*1024)
296-
* `windowBits`
297-
* `level` (compression only)
298-
* `memLevel` (compression only)
299-
* `strategy` (compression only)
300-
* `dictionary` (deflate/inflate only, empty dictionary by default)
296+
* `flush` {integer} (default: `zlib.constants.Z_NO_FLUSH`)
297+
* `finishFlush` {integer} (default: `zlib.constants.Z_FINISH`)
298+
* `chunkSize` {integer} (default: 16 × 1024)
299+
* `windowBits` {integer}
300+
* `level` {integer} (compression only)
301+
* `memLevel` {integer} (compression only)
302+
* `strategy` {integer} (compression only)
303+
* `dictionary` {Buffer|Uint8Array} (deflate/inflate only, empty dictionary by
304+
default)
301305

302306
See the description of `deflateInit2` and `inflateInit2` at
303307
<http://zlib.net/manual.html#Advanced> for more information on these.
@@ -473,89 +477,159 @@ Returns a new [Unzip][] object with an [options][].
473477

474478
<!--type=misc-->
475479

476-
All of these take a [Buffer][] or string as the first argument, an optional
477-
second argument to supply options to the `zlib` classes and will call the
478-
supplied callback with `callback(error, result)`.
480+
All of these take a [Buffer][], [Uint8Array][], or string as the first
481+
argument, an optional second argument to supply options to the `zlib` classes
482+
and will call the supplied callback with `callback(error, result)`.
479483

480484
Every method has a `*Sync` counterpart, which accept the same arguments, but
481485
without a callback.
482486

483-
### zlib.deflate(buf[, options], callback)
487+
### zlib.deflate(buffer[, options], callback)
484488
<!-- YAML
485489
added: v0.6.0
490+
changes:
491+
- version: REPLACEME
492+
pr-url: https://github.com/nodejs/node/pull/12001
493+
description: The `buffer` parameter can be an Uint8Array now.
486494
-->
487-
### zlib.deflateSync(buf[, options])
495+
### zlib.deflateSync(buffer[, options])
488496
<!-- YAML
489497
added: v0.11.12
498+
changes:
499+
- version: REPLACEME
500+
pr-url: https://github.com/nodejs/node/pull/12001
501+
description: The `buffer` parameter can be an Uint8Array now.
490502
-->
491503

492-
Compress a [Buffer][] or string with [Deflate][].
504+
- `buffer` {Buffer|Uint8Array|string}
505+
506+
Compress a chunk of data with [Deflate][].
493507

494-
### zlib.deflateRaw(buf[, options], callback)
508+
### zlib.deflateRaw(buffer[, options], callback)
495509
<!-- YAML
496510
added: v0.6.0
511+
changes:
512+
- version: REPLACEME
513+
pr-url: https://github.com/nodejs/node/pull/12001
514+
description: The `buffer` parameter can be an Uint8Array now.
497515
-->
498-
### zlib.deflateRawSync(buf[, options])
516+
### zlib.deflateRawSync(buffer[, options])
499517
<!-- YAML
500518
added: v0.11.12
519+
changes:
520+
- version: REPLACEME
521+
pr-url: https://github.com/nodejs/node/pull/12001
522+
description: The `buffer` parameter can be an Uint8Array now.
501523
-->
502524

503-
Compress a [Buffer][] or string with [DeflateRaw][].
525+
- `buffer` {Buffer|Uint8Array|string}
526+
527+
Compress a chunk of data with [DeflateRaw][].
504528

505-
### zlib.gunzip(buf[, options], callback)
529+
### zlib.gunzip(buffer[, options], callback)
506530
<!-- YAML
507531
added: v0.6.0
532+
changes:
533+
- version: REPLACEME
534+
pr-url: https://github.com/nodejs/node/pull/12001
535+
description: The `buffer` parameter can be an Uint8Array now.
508536
-->
509-
### zlib.gunzipSync(buf[, options])
537+
### zlib.gunzipSync(buffer[, options])
510538
<!-- YAML
511539
added: v0.11.12
540+
changes:
541+
- version: REPLACEME
542+
pr-url: https://github.com/nodejs/node/pull/12001
543+
description: The `buffer` parameter can be an Uint8Array now.
512544
-->
513545

514-
Decompress a [Buffer][] or string with [Gunzip][].
546+
- `buffer` {Buffer|Uint8Array|string}
547+
548+
Decompress a chunk of data with [Gunzip][].
515549

516-
### zlib.gzip(buf[, options], callback)
550+
### zlib.gzip(buffer[, options], callback)
517551
<!-- YAML
518552
added: v0.6.0
553+
changes:
554+
- version: REPLACEME
555+
pr-url: https://github.com/nodejs/node/pull/12001
556+
description: The `buffer` parameter can be an Uint8Array now.
519557
-->
520-
### zlib.gzipSync(buf[, options])
558+
### zlib.gzipSync(buffer[, options])
521559
<!-- YAML
522560
added: v0.11.12
561+
changes:
562+
- version: REPLACEME
563+
pr-url: https://github.com/nodejs/node/pull/12001
564+
description: The `buffer` parameter can be an Uint8Array now.
523565
-->
524566

525-
Compress a [Buffer][] or string with [Gzip][].
567+
- `buffer` {Buffer|Uint8Array|string}
526568

527-
### zlib.inflate(buf[, options], callback)
569+
Compress a chunk of data with [Gzip][].
570+
571+
### zlib.inflate(buffer[, options], callback)
528572
<!-- YAML
529573
added: v0.6.0
574+
changes:
575+
- version: REPLACEME
576+
pr-url: https://github.com/nodejs/node/pull/12001
577+
description: The `buffer` parameter can be an Uint8Array now.
530578
-->
531-
### zlib.inflateSync(buf[, options])
579+
### zlib.inflateSync(buffer[, options])
532580
<!-- YAML
533581
added: v0.11.12
582+
changes:
583+
- version: REPLACEME
584+
pr-url: https://github.com/nodejs/node/pull/12001
585+
description: The `buffer` parameter can be an Uint8Array now.
534586
-->
535587

536-
Decompress a [Buffer][] or string with [Inflate][].
588+
- `buffer` {Buffer|Uint8Array|string}
537589

538-
### zlib.inflateRaw(buf[, options], callback)
590+
Decompress a chunk of data with [Inflate][].
591+
592+
### zlib.inflateRaw(buffer[, options], callback)
539593
<!-- YAML
540594
added: v0.6.0
595+
changes:
596+
- version: REPLACEME
597+
pr-url: https://github.com/nodejs/node/pull/12001
598+
description: The `buffer` parameter can be an Uint8Array now.
541599
-->
542-
### zlib.inflateRawSync(buf[, options])
600+
### zlib.inflateRawSync(buffer[, options])
543601
<!-- YAML
544602
added: v0.11.12
603+
changes:
604+
- version: REPLACEME
605+
pr-url: https://github.com/nodejs/node/pull/12001
606+
description: The `buffer` parameter can be an Uint8Array now.
545607
-->
546608

547-
Decompress a [Buffer][] or string with [InflateRaw][].
609+
- `buffer` {Buffer|Uint8Array|string}
610+
611+
Decompress a chunk of data with [InflateRaw][].
548612

549-
### zlib.unzip(buf[, options], callback)
613+
### zlib.unzip(buffer[, options], callback)
550614
<!-- YAML
551615
added: v0.6.0
616+
changes:
617+
- version: REPLACEME
618+
pr-url: https://github.com/nodejs/node/pull/12001
619+
description: The `buffer` parameter can be an Uint8Array now.
552620
-->
553-
### zlib.unzipSync(buf[, options])
621+
### zlib.unzipSync(buffer[, options])
554622
<!-- YAML
555623
added: v0.11.12
624+
changes:
625+
- version: REPLACEME
626+
pr-url: https://github.com/nodejs/node/pull/12001
627+
description: The `buffer` parameter can be an Uint8Array now.
556628
-->
557629

558-
Decompress a [Buffer][] or string with [Unzip][].
630+
- `buf` {Buffer|Uint8Array|string}
631+
632+
Decompress a chunk of data with [Unzip][].
559633

560634
[`Accept-Encoding`]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
561635
[`Content-Encoding`]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
@@ -571,3 +645,4 @@ Decompress a [Buffer][] or string with [Unzip][].
571645
[Unzip]: #zlib_class_zlib_unzip
572646
[`.flush()`]: #zlib_zlib_flush_kind_callback
573647
[Buffer]: buffer.html
648+
[Uint8Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array

lib/zlib.js

+14-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
const Buffer = require('buffer').Buffer;
2525
const internalUtil = require('internal/util');
2626
const Transform = require('_stream_transform');
27+
const { isUint8Array } = process.binding('util');
2728
const binding = process.binding('zlib');
2829
const assert = require('assert').ok;
2930
const kMaxLength = require('buffer').kMaxLength;
@@ -78,6 +79,13 @@ function isInvalidStrategy(strategy) {
7879
}
7980

8081
function zlibBuffer(engine, buffer, callback) {
82+
// Streams do not support non-Buffer Uint8Arrays yet. Convert it to a
83+
// Buffer without copying.
84+
if (isUint8Array(buffer) &&
85+
!(Object.getPrototypeOf(buffer) === Buffer.prototype ||
86+
buffer instanceof Buffer))
87+
buffer = Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength);
88+
8189
var buffers = [];
8290
var nread = 0;
8391

@@ -121,8 +129,9 @@ function zlibBuffer(engine, buffer, callback) {
121129
function zlibBufferSync(engine, buffer) {
122130
if (typeof buffer === 'string')
123131
buffer = Buffer.from(buffer);
124-
if (!(buffer instanceof Buffer))
125-
throw new TypeError('Not a string or buffer');
132+
else if (!isUint8Array(buffer))
133+
throw new TypeError('"buffer" argument must be a string, Buffer, or ' +
134+
'Uint8Array');
126135

127136
var flushFlag = engine._finishFlushFlag;
128137

@@ -205,9 +214,9 @@ class Zlib extends Transform {
205214
throw new TypeError('Invalid strategy: ' + opts.strategy);
206215

207216
if (opts.dictionary) {
208-
if (!(opts.dictionary instanceof Buffer)) {
217+
if (!isUint8Array(opts.dictionary)) {
209218
throw new TypeError(
210-
'Invalid dictionary: it should be a Buffer instance');
219+
'Invalid dictionary: it should be a Buffer or an Uint8Array');
211220
}
212221
}
213222

@@ -302,7 +311,7 @@ class Zlib extends Transform {
302311
var ending = ws.ending || ws.ended;
303312
var last = ending && (!chunk || ws.length === chunk.length);
304313

305-
if (chunk !== null && !(chunk instanceof Buffer))
314+
if (chunk !== null && !isUint8Array(chunk))
306315
return cb(new TypeError('invalid input'));
307316

308317
if (!this._handle)

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

+41-40
Original file line numberDiff line numberDiff line change
@@ -22,59 +22,60 @@
2222
'use strict';
2323
// test convenience methods with and without options supplied
2424

25-
require('../common');
25+
const common = require('../common');
2626
const assert = require('assert');
2727
const zlib = require('zlib');
2828

29-
let hadRun = 0;
30-
31-
const expect = 'blahblahblahblahblahblah';
29+
const expectStr = 'blahblahblahblahblahblah';
30+
const expectBuf = Buffer.from(expectStr);
31+
const expectUint8Array = new Uint8Array([...expectBuf]);
3232
const opts = {
3333
level: 9,
3434
chunkSize: 1024,
3535
};
3636

37-
[
37+
for (const method of [
3838
['gzip', 'gunzip'],
3939
['gzip', 'unzip'],
4040
['deflate', 'inflate'],
4141
['deflateRaw', 'inflateRaw'],
42-
].forEach(function(method) {
43-
44-
zlib[method[0]](expect, opts, function(err, result) {
45-
zlib[method[1]](result, opts, function(err, result) {
46-
assert.strictEqual(result.toString(), expect,
47-
'Should get original string after ' +
48-
method[0] + '/' + method[1] + ' with options.');
49-
hadRun++;
50-
});
51-
});
52-
53-
zlib[method[0]](expect, function(err, result) {
54-
zlib[method[1]](result, function(err, result) {
55-
assert.strictEqual(result.toString(), expect,
56-
'Should get original string after ' +
57-
method[0] + '/' + method[1] + ' without options.');
58-
hadRun++;
59-
});
60-
});
42+
]) {
43+
for (const [type, expect] of [
44+
['string', expectStr],
45+
['Buffer', expectBuf],
46+
['Uint8Array', expectUint8Array]
47+
]) {
48+
zlib[method[0]](expect, opts, common.mustCall((err, result) => {
49+
zlib[method[1]](result, opts, common.mustCall((err, result) => {
50+
assert.strictEqual(result.toString(), expectStr,
51+
`Should get original string after ${method[0]}/` +
52+
`${method[1]} ${type} with options.`);
53+
}));
54+
}));
6155

62-
let result = zlib[method[0] + 'Sync'](expect, opts);
63-
result = zlib[method[1] + 'Sync'](result, opts);
64-
assert.strictEqual(result.toString(), expect,
65-
'Should get original string after ' +
66-
method[0] + '/' + method[1] + ' with options.');
67-
hadRun++;
56+
zlib[method[0]](expect, common.mustCall((err, result) => {
57+
zlib[method[1]](result, common.mustCall((err, result) => {
58+
assert.strictEqual(result.toString(), expectStr,
59+
`Should get original string after ${method[0]}/` +
60+
`${method[1]} ${type} without options.`);
61+
}));
62+
}));
6863

69-
result = zlib[method[0] + 'Sync'](expect);
70-
result = zlib[method[1] + 'Sync'](result);
71-
assert.strictEqual(result.toString(), expect,
72-
'Should get original string after ' +
73-
method[0] + '/' + method[1] + ' without options.');
74-
hadRun++;
64+
{
65+
const compressed = zlib[method[0] + 'Sync'](expect, opts);
66+
const decompressed = zlib[method[1] + 'Sync'](compressed, opts);
67+
assert.strictEqual(decompressed.toString(), expectStr,
68+
`Should get original string after ${method[0]}Sync/` +
69+
`${method[1]}Sync ${type} with options.`);
70+
}
7571

76-
});
7772

78-
process.on('exit', function() {
79-
assert.strictEqual(hadRun, 16, 'expect 16 compressions');
80-
});
73+
{
74+
const compressed = zlib[method[0] + 'Sync'](expect);
75+
const decompressed = zlib[method[1] + 'Sync'](compressed);
76+
assert.strictEqual(decompressed.toString(), expectStr,
77+
`Should get original string after ${method[0]}Sync/` +
78+
`${method[1]}Sync ${type} without options.`);
79+
}
80+
}
81+
}

test/parallel/test-zlib-deflate-constructors.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,5 +107,5 @@ assert.throws(
107107
// Throws if opts.dictionary is not a Buffer
108108
assert.throws(
109109
() => { new zlib.Deflate({dictionary: 'not a buffer'}); },
110-
/^TypeError: Invalid dictionary: it should be a Buffer instance$/
110+
/^TypeError: Invalid dictionary: it should be a Buffer or an Uint8Array$/
111111
);

0 commit comments

Comments
 (0)