Skip to content

Commit 4ae630a

Browse files
authored
[perf] Avoid using Buffer.concat() (#1026)
1 parent a70c6d0 commit 4ae630a

File tree

3 files changed

+56
-43
lines changed

3 files changed

+56
-43
lines changed

lib/BufferUtil.js

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,32 @@
66
* MIT Licensed
77
*/
88

9+
/**
10+
* Merges an array of buffers into a new buffer.
11+
*
12+
* @param {Buffer[]} list The array of buffers to concat
13+
* @param {Number} totalLength The total length of buffers in the list
14+
* @return {Buffer} The resulting buffer
15+
* @public
16+
*/
17+
const concat = (list, totalLength) => {
18+
const target = Buffer.allocUnsafe(totalLength);
19+
var offset = 0;
20+
21+
for (var i = 0; i < list.length; i++) {
22+
const buf = list[i];
23+
buf.copy(target, offset);
24+
offset += buf.length;
25+
}
26+
27+
return target;
28+
};
29+
930
try {
1031
const bufferUtil = require('bufferutil');
1132

12-
module.exports = bufferUtil.BufferUtil || bufferUtil;
33+
module.exports = Object.assign({ concat }, bufferUtil.BufferUtil || bufferUtil);
1334
} catch (e) {
14-
/**
15-
* Merges an array of buffers into a target buffer.
16-
*
17-
* @param {Buffer} target The target buffer
18-
* @param {Buffer[]} buffers The array of buffers to merge
19-
* @public
20-
*/
21-
const merge = (target, buffers) => {
22-
var offset = 0;
23-
for (var i = 0; i < buffers.length; i++) {
24-
const buf = buffers[i];
25-
buf.copy(target, offset);
26-
offset += buf.length;
27-
}
28-
};
29-
3035
/**
3136
* Masks a buffer using the given mask.
3237
*
@@ -58,5 +63,5 @@ try {
5863
}
5964
};
6065

61-
module.exports = { merge, mask, unmask };
66+
module.exports = { concat, mask, unmask };
6267
}

lib/PerMessageDeflate.js

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22

33
const zlib = require('zlib');
44

5+
const bufferUtil = require('./BufferUtil');
6+
57
const AVAILABLE_WINDOW_BITS = [8, 9, 10, 11, 12, 13, 14, 15];
68
const DEFAULT_WINDOW_BITS = 15;
79
const DEFAULT_MEM_LEVEL = 8;
810
const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
911
const EMPTY_BLOCK = Buffer.from([0x00]);
1012

1113
/**
12-
* Per-message Compression Extensions implementation
14+
* Per-message Deflate implementation.
1315
*/
1416
class PerMessageDeflate {
1517
constructor (options, isServer, maxPayload) {
@@ -22,12 +24,15 @@ class PerMessageDeflate {
2224
this.threshold = this._options.threshold === undefined ? 1024 : this._options.threshold;
2325
}
2426

27+
static get extensionName () {
28+
return 'permessage-deflate';
29+
}
30+
2531
/**
2632
* Create extension parameters offer
2733
*
28-
* @api public
34+
* @public
2935
*/
30-
3136
offer () {
3237
var params = {};
3338
if (this._options.serverNoContextTakeover) {
@@ -50,7 +55,7 @@ class PerMessageDeflate {
5055
/**
5156
* Accept extension offer
5257
*
53-
* @api public
58+
* @public
5459
*/
5560
accept (paramsList) {
5661
paramsList = this.normalizeParams(paramsList);
@@ -69,7 +74,7 @@ class PerMessageDeflate {
6974
/**
7075
* Releases all resources used by the extension
7176
*
72-
* @api public
77+
* @public
7378
*/
7479
cleanup () {
7580
if (this._inflate) {
@@ -93,9 +98,8 @@ class PerMessageDeflate {
9398
/**
9499
* Accept extension offer from client
95100
*
96-
* @api private
101+
* @private
97102
*/
98-
99103
acceptAsServer (paramsList) {
100104
var accepted = {};
101105
var result = paramsList.some((params) => {
@@ -147,9 +151,8 @@ class PerMessageDeflate {
147151
/**
148152
* Accept extension response from server
149153
*
150-
* @api privaye
154+
* @private
151155
*/
152-
153156
acceptAsClient (paramsList) {
154157
var params = paramsList[0];
155158
if (this._options.clientNoContextTakeover != null) {
@@ -172,9 +175,8 @@ class PerMessageDeflate {
172175
/**
173176
* Normalize extensions parameters
174177
*
175-
* @api private
178+
* @private
176179
*/
177-
178180
normalizeParams (paramsList) {
179181
return paramsList.map((params) => {
180182
Object.keys(params).forEach((key) => {
@@ -276,26 +278,25 @@ class PerMessageDeflate {
276278
this._inflate.flush(() => {
277279
cleanup();
278280
if (err) callback(err);
279-
else callback(null, Buffer.concat(buffers, totalLength));
281+
else callback(null, bufferUtil.concat(buffers, totalLength));
280282
});
281283
}
282284

283285
/**
284286
* Compress message
285287
*
286-
* @api public
288+
* @public
287289
*/
288-
289290
compress (data, fin, callback) {
290291
if (!data || data.length === 0) {
291292
process.nextTick(callback, null, EMPTY_BLOCK);
292293
return;
293294
}
294295

295-
var endpoint = this._isServer ? 'server' : 'client';
296+
const endpoint = this._isServer ? 'server' : 'client';
296297

297298
if (!this._deflate) {
298-
var maxWindowBits = this.params[endpoint + '_max_window_bits'];
299+
const maxWindowBits = this.params[`${endpoint}_max_window_bits`];
299300
this._deflate = zlib.createDeflateRaw({
300301
flush: zlib.Z_SYNC_FLUSH,
301302
windowBits: typeof maxWindowBits === 'number' ? maxWindowBits : DEFAULT_WINDOW_BITS,
@@ -304,19 +305,30 @@ class PerMessageDeflate {
304305
}
305306
this._deflate.writeInProgress = true;
306307

308+
var totalLength = 0;
307309
const buffers = [];
308310

309-
const onData = (data) => buffers.push(data);
311+
const onData = (data) => {
312+
totalLength += data.length;
313+
buffers.push(data);
314+
};
315+
310316
const onError = (err) => {
311317
cleanup();
312318
callback(err);
313319
};
320+
314321
const cleanup = () => {
315322
if (!this._deflate) return;
323+
316324
this._deflate.removeListener('error', onError);
317325
this._deflate.removeListener('data', onData);
318326
this._deflate.writeInProgress = false;
319-
if ((fin && this.params[endpoint + '_no_context_takeover']) || this._deflate.pendingClose) {
327+
328+
if (
329+
(fin && this.params[`${endpoint}_no_context_takeover`]) ||
330+
this._deflate.pendingClose
331+
) {
320332
this._deflate.close();
321333
this._deflate = null;
322334
}
@@ -326,15 +338,11 @@ class PerMessageDeflate {
326338
this._deflate.write(data);
327339
this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
328340
cleanup();
329-
var data = Buffer.concat(buffers);
330-
if (fin) {
331-
data = data.slice(0, data.length - 4);
332-
}
341+
var data = bufferUtil.concat(buffers, totalLength);
342+
if (fin) data = data.slice(0, data.length - 4);
333343
callback(null, data);
334344
});
335345
}
336346
}
337347

338-
PerMessageDeflate.extensionName = 'permessage-deflate';
339-
340348
module.exports = PerMessageDeflate;

lib/Receiver.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ module.exports = Receiver;
532532
*/
533533
function toBuffer (fragments, messageLength) {
534534
if (fragments.length === 1) return fragments[0];
535-
if (fragments.length > 1) return Buffer.concat(fragments, messageLength);
535+
if (fragments.length > 1) return bufferUtil.concat(fragments, messageLength);
536536
return constants.EMPTY_BUFFER;
537537
}
538538

0 commit comments

Comments
 (0)