Skip to content

Commit 9a627a4

Browse files
committed
benchmark,test: add brotli
Co-authored-by: Hackzzila <[email protected]> PR-URL: #24938 Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Jan Krems <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Myles Borins <[email protected]> Reviewed-By: Ali Ijaz Sheikh <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]>
1 parent 7edf8c7 commit 9a627a4

16 files changed

+267
-49
lines changed

benchmark/zlib/creation.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ const zlib = require('zlib');
44

55
const bench = common.createBenchmark(main, {
66
type: [
7-
'Deflate', 'DeflateRaw', 'Inflate', 'InflateRaw', 'Gzip', 'Gunzip', 'Unzip'
7+
'Deflate', 'DeflateRaw', 'Inflate', 'InflateRaw', 'Gzip', 'Gunzip', 'Unzip',
8+
'BrotliCompress', 'BrotliDecompress'
89
],
910
options: ['true', 'false'],
1011
n: [5e5]

benchmark/zlib/pipe.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@ const zlib = require('zlib');
66
const bench = common.createBenchmark(main, {
77
inputLen: [1024],
88
duration: [5],
9-
type: ['string', 'buffer']
9+
type: ['string', 'buffer'],
10+
algorithm: ['gzip', 'brotli']
1011
});
1112

12-
function main({ inputLen, duration, type }) {
13+
function main({ inputLen, duration, type, algorithm }) {
1314
const buffer = Buffer.alloc(inputLen, fs.readFileSync(__filename));
1415
const chunk = type === 'buffer' ? buffer : buffer.toString('utf8');
1516

16-
const input = zlib.createGzip();
17-
const output = zlib.createGunzip();
17+
const input = algorithm === 'gzip' ?
18+
zlib.createGzip() : zlib.createBrotliCompress();
19+
const output = algorithm === 'gzip' ?
20+
zlib.createGunzip() : zlib.createBrotliDecompress();
1821

1922
let readFromOutput = 0;
2023
input.pipe(output);

test/fixtures/person.jpg.br

44.1 KB
Binary file not shown.
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
const zlib = require('zlib');
5+
const fixtures = require('../common/fixtures');
6+
7+
const file = fixtures.readSync('person.jpg');
8+
const chunkSize = 16;
9+
const deflater = new zlib.BrotliCompress();
10+
11+
const chunk = file.slice(0, chunkSize);
12+
const expectedFull = Buffer.from('iweA/9j/4AAQSkZJRgABAQEASA==', 'base64');
13+
let actualFull;
14+
15+
deflater.write(chunk, function() {
16+
deflater.flush(function() {
17+
const bufs = [];
18+
let buf;
19+
while (buf = deflater.read())
20+
bufs.push(buf);
21+
actualFull = Buffer.concat(bufs);
22+
});
23+
});
24+
25+
process.once('exit', function() {
26+
assert.deepStrictEqual(actualFull, expectedFull);
27+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict';
2+
// Test unzipping a file that was created with a non-node brotli lib,
3+
// piped in as fast as possible.
4+
5+
const common = require('../common');
6+
const assert = require('assert');
7+
const zlib = require('zlib');
8+
const path = require('path');
9+
const fixtures = require('../common/fixtures');
10+
11+
const tmpdir = require('../common/tmpdir');
12+
tmpdir.refresh();
13+
14+
const decompress = new zlib.BrotliDecompress();
15+
16+
const fs = require('fs');
17+
18+
const fixture = fixtures.path('person.jpg.br');
19+
const unzippedFixture = fixtures.path('person.jpg');
20+
const outputFile = path.resolve(tmpdir.path, 'person.jpg');
21+
const expect = fs.readFileSync(unzippedFixture);
22+
const inp = fs.createReadStream(fixture);
23+
const out = fs.createWriteStream(outputFile);
24+
25+
inp.pipe(decompress).pipe(out);
26+
out.on('close', common.mustCall(() => {
27+
const actual = fs.readFileSync(outputFile);
28+
assert.strictEqual(actual.length, expect.length);
29+
for (let i = 0, l = actual.length; i < l; i++) {
30+
assert.strictEqual(actual[i], expect[i], `byte[${i}]`);
31+
}
32+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
'use strict';
2+
// Test compressing and uncompressing a string with brotli
3+
4+
const common = require('../common');
5+
const assert = require('assert');
6+
const zlib = require('zlib');
7+
8+
const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' +
9+
't. Morbi faucibus, purus at gravida dictum, libero arcu ' +
10+
'convallis lacus, in commodo libero metus eu nisi. Nullam' +
11+
' commodo, neque nec porta placerat, nisi est fermentum a' +
12+
'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' +
13+
'n non diam orci. Proin quis elit turpis. Suspendisse non' +
14+
' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' +
15+
'm arcu mi, sodales non suscipit id, ultrices ut massa. S' +
16+
'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. ';
17+
const expectedBase64Compress = 'G/gBQBwHdky2aHV5KK9Snf05//1pPdmNw/7232fnIm1IB' +
18+
'K1AA8RsN8OB8Nb7Lpgk3UWWUlzQXZyHQeBBbXMTQXC1j7' +
19+
'wg3LJs9LqOGHRH2bj/a2iCTLLx8hBOyTqgoVuD1e+Qqdn' +
20+
'f1rkUNyrWq6LtOhWgxP3QUwdhKGdZm3rJWaDDBV7+pDk1' +
21+
'MIkrmjp4ma2xVi5MsgJScA3tP1I7mXeby6MELozrwoBQD' +
22+
'mVTnEAicZNj4lkGqntJe2qSnGyeMmcFgraK94vCg/4iLu' +
23+
'Tw5RhKhnVY++dZ6niUBmRqIutsjf5TzwF5iAg8a9UkjF5' +
24+
'2eZ0tB2vo6v8SqVfNMkBmmhxr0NT9LkYF69aEjlYzj7IE' +
25+
'KmEUQf1HBogRYhFIt4ymRNEgHAIzOyNEsQM=';
26+
27+
zlib.brotliCompress(inputString, common.mustCall((err, buffer) => {
28+
assert.strictEqual(buffer.toString('base64'), expectedBase64Compress);
29+
}));
30+
31+
const buffer = Buffer.from(expectedBase64Compress, 'base64');
32+
zlib.brotliDecompress(buffer, common.mustCall((err, buffer) => {
33+
assert.strictEqual(buffer.toString(), inputString);
34+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
require('../common');
3+
4+
// This test ensures that zlib throws a RangeError if the final buffer needs to
5+
// be larger than kMaxLength and concatenation fails.
6+
// https://github.com/nodejs/node/pull/1811
7+
8+
const assert = require('assert');
9+
10+
// Change kMaxLength for zlib to trigger the error without having to allocate
11+
// large Buffers.
12+
const buffer = require('buffer');
13+
const oldkMaxLength = buffer.kMaxLength;
14+
buffer.kMaxLength = 128;
15+
const zlib = require('zlib');
16+
buffer.kMaxLength = oldkMaxLength;
17+
18+
const encoded = Buffer.from('G38A+CXCIrFAIAM=', 'base64');
19+
20+
// Async
21+
zlib.brotliDecompress(encoded, function(err) {
22+
assert.ok(err instanceof RangeError);
23+
});
24+
25+
// Sync
26+
assert.throws(function() {
27+
zlib.brotliDecompressSync(encoded);
28+
}, RangeError);

test/parallel/test-zlib-brotli.js

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
'use strict';
2+
const common = require('../common');
3+
const fixtures = require('../common/fixtures');
4+
const assert = require('assert');
5+
const zlib = require('zlib');
6+
7+
// Test some brotli-specific properties of the brotli streams that can not
8+
// be easily covered through expanding zlib-only tests.
9+
10+
const sampleBuffer = fixtures.readSync('/pss-vectors.json');
11+
12+
{
13+
// Test setting the quality parameter at stream creation:
14+
const sizes = [];
15+
for (let quality = zlib.constants.BROTLI_MIN_QUALITY;
16+
quality <= zlib.constants.BROTLI_MAX_QUALITY;
17+
quality++) {
18+
const encoded = zlib.brotliCompressSync(sampleBuffer, {
19+
params: {
20+
[zlib.constants.BROTLI_PARAM_QUALITY]: quality
21+
}
22+
});
23+
sizes.push(encoded.length);
24+
}
25+
26+
// Increasing quality should roughly correspond to decreasing compressed size:
27+
for (let i = 0; i < sizes.length - 1; i++) {
28+
assert(sizes[i + 1] <= sizes[i] * 1.05, sizes); // 5 % margin of error.
29+
}
30+
assert(sizes[0] > sizes[sizes.length - 1], sizes);
31+
}
32+
33+
{
34+
// Test that setting out-of-bounds option values or keys fails.
35+
common.expectsError(() => {
36+
zlib.createBrotliCompress({
37+
params: {
38+
10000: 0
39+
}
40+
});
41+
}, {
42+
code: 'ERR_BROTLI_INVALID_PARAM',
43+
type: RangeError,
44+
message: '10000 is not a valid Brotli parameter'
45+
});
46+
47+
// Test that accidentally using duplicate keys fails.
48+
common.expectsError(() => {
49+
zlib.createBrotliCompress({
50+
params: {
51+
'0': 0,
52+
'00': 0
53+
}
54+
});
55+
}, {
56+
code: 'ERR_BROTLI_INVALID_PARAM',
57+
type: RangeError,
58+
message: '00 is not a valid Brotli parameter'
59+
});
60+
61+
common.expectsError(() => {
62+
zlib.createBrotliCompress({
63+
params: {
64+
// This is a boolean flag
65+
[zlib.constants.BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING]: 42
66+
}
67+
});
68+
}, {
69+
code: 'ERR_ZLIB_INITIALIZATION_FAILED',
70+
type: Error,
71+
message: 'Initialization failed'
72+
});
73+
}

test/parallel/test-zlib-bytes-read.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ for (const method of [
3131
['createGzip', 'createGunzip', false],
3232
['createGzip', 'createUnzip', false],
3333
['createDeflate', 'createInflate', true],
34-
['createDeflateRaw', 'createInflateRaw', true]
34+
['createDeflateRaw', 'createInflateRaw', true],
35+
['createBrotliCompress', 'createBrotliDecompress', true]
3536
]) {
3637
let compWriter;
3738
let compData = Buffer.alloc(0);

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

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ for (const [type, expect] of [
5252
['gzip', 'unzip', 'Gzip', 'Unzip'],
5353
['deflate', 'inflate', 'Deflate', 'Inflate'],
5454
['deflateRaw', 'inflateRaw', 'DeflateRaw', 'InflateRaw'],
55+
['brotliCompress', 'brotliDecompress',
56+
'BrotliCompress', 'BrotliDecompress'],
5557
]) {
5658
zlib[method[0]](expect, opts, common.mustCall((err, result) => {
5759
zlib[method[1]](result, opts, common.mustCall((err, result) => {

test/parallel/test-zlib-empty-buffer.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ const emptyBuffer = Buffer.alloc(0);
1010
[ zlib.deflateRawSync, zlib.inflateRawSync, 'raw sync' ],
1111
[ zlib.deflateSync, zlib.inflateSync, 'deflate sync' ],
1212
[ zlib.gzipSync, zlib.gunzipSync, 'gzip sync' ],
13+
[ zlib.brotliCompressSync, zlib.brotliDecompressSync, 'br sync' ],
1314
[ promisify(zlib.deflateRaw), promisify(zlib.inflateRaw), 'raw' ],
1415
[ promisify(zlib.deflate), promisify(zlib.inflate), 'deflate' ],
15-
[ promisify(zlib.gzip), promisify(zlib.gunzip), 'gzip' ]
16+
[ promisify(zlib.gzip), promisify(zlib.gunzip), 'gzip' ],
17+
[ promisify(zlib.brotliCompress), promisify(zlib.brotliDecompress), 'br' ]
1618
]) {
1719
const compressed = await compress(emptyBuffer);
1820
const decompressed = await decompress(compressed);

test/parallel/test-zlib-invalid-input.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ const unzips = [
3838
zlib.Unzip(),
3939
zlib.Gunzip(),
4040
zlib.Inflate(),
41-
zlib.InflateRaw()
41+
zlib.InflateRaw(),
42+
zlib.BrotliDecompress()
4243
];
4344

4445
nonStringInputs.forEach(common.mustCall((input) => {

test/parallel/test-zlib-random-byte-pipes.js

+15-11
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,18 @@ class HashStream extends Stream {
141141
}
142142
}
143143

144-
145-
const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 });
146-
const out = new HashStream();
147-
const gzip = zlib.createGzip();
148-
const gunz = zlib.createGunzip();
149-
150-
inp.pipe(gzip).pipe(gunz).pipe(out);
151-
152-
out.on('data', common.mustCall((c) => {
153-
assert.strictEqual(c, inp._hash, `Hash '${c}' equals '${inp._hash}'.`);
154-
}));
144+
for (const [ createCompress, createDecompress ] of [
145+
[ zlib.createGzip, zlib.createGunzip ],
146+
[ zlib.createBrotliCompress, zlib.createBrotliDecompress ],
147+
]) {
148+
const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 });
149+
const out = new HashStream();
150+
const gzip = createCompress();
151+
const gunz = createDecompress();
152+
153+
inp.pipe(gzip).pipe(gunz).pipe(out);
154+
155+
out.on('data', common.mustCall((c) => {
156+
assert.strictEqual(c, inp._hash, `Hash '${c}' equals '${inp._hash}'.`);
157+
}));
158+
}

test/parallel/test-zlib-write-after-flush.js

+21-16
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,27 @@ const common = require('../common');
2424
const assert = require('assert');
2525
const zlib = require('zlib');
2626

27-
const gzip = zlib.createGzip();
28-
const gunz = zlib.createUnzip();
27+
for (const [ createCompress, createDecompress ] of [
28+
[ zlib.createGzip, zlib.createGunzip ],
29+
[ zlib.createBrotliCompress, zlib.createBrotliDecompress ],
30+
]) {
31+
const gzip = createCompress();
32+
const gunz = createDecompress();
2933

30-
gzip.pipe(gunz);
34+
gzip.pipe(gunz);
3135

32-
let output = '';
33-
const input = 'A line of data\n';
34-
gunz.setEncoding('utf8');
35-
gunz.on('data', (c) => output += c);
36-
gunz.on('end', common.mustCall(() => {
37-
assert.strictEqual(output, input);
38-
assert.strictEqual(gzip._nextFlush, -1);
39-
}));
36+
let output = '';
37+
const input = 'A line of data\n';
38+
gunz.setEncoding('utf8');
39+
gunz.on('data', (c) => output += c);
40+
gunz.on('end', common.mustCall(() => {
41+
assert.strictEqual(output, input);
42+
assert.strictEqual(gzip._nextFlush, -1);
43+
}));
4044

41-
// Make sure that flush/write doesn't trigger an assert failure
42-
gzip.flush();
43-
gzip.write(input);
44-
gzip.end();
45-
gunz.read(0);
45+
// Make sure that flush/write doesn't trigger an assert failure
46+
gzip.flush();
47+
gzip.write(input);
48+
gzip.end();
49+
gunz.read(0);
50+
}

test/parallel/test-zlib-zero-byte.js

+17-13
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,22 @@
2222
'use strict';
2323
const common = require('../common');
2424
const assert = require('assert');
25-
2625
const zlib = require('zlib');
27-
const gz = zlib.Gzip();
28-
const emptyBuffer = Buffer.alloc(0);
29-
let received = 0;
30-
gz.on('data', function(c) {
31-
received += c.length;
32-
});
3326

34-
gz.on('end', common.mustCall(function() {
35-
assert.strictEqual(received, 20);
36-
}));
37-
gz.on('finish', common.mustCall());
38-
gz.write(emptyBuffer);
39-
gz.end();
27+
for (const Compressor of [ zlib.Gzip, zlib.BrotliCompress ]) {
28+
const gz = Compressor();
29+
const emptyBuffer = Buffer.alloc(0);
30+
let received = 0;
31+
gz.on('data', function(c) {
32+
received += c.length;
33+
});
34+
35+
gz.on('end', common.mustCall(function() {
36+
const expected = Compressor === zlib.Gzip ? 20 : 1;
37+
assert.strictEqual(received, expected,
38+
`${received}, ${expected}, ${Compressor.name}`);
39+
}));
40+
gz.on('finish', common.mustCall());
41+
gz.write(emptyBuffer);
42+
gz.end();
43+
}

test/parallel/test-zlib.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ let zlibPairs = [
3232
[zlib.Gzip, zlib.Gunzip],
3333
[zlib.Deflate, zlib.Unzip],
3434
[zlib.Gzip, zlib.Unzip],
35-
[zlib.DeflateRaw, zlib.InflateRaw]
35+
[zlib.DeflateRaw, zlib.InflateRaw],
36+
[zlib.BrotliCompress, zlib.BrotliDecompress],
3637
];
3738

3839
// how fast to trickle through the slowstream

0 commit comments

Comments
 (0)