Skip to content

Commit 9057814

Browse files
mscdextargos
authored andcommitted
buffer: improve copy() performance
PR-URL: #29066 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 9ead4ec commit 9057814

File tree

4 files changed

+105
-10
lines changed

4 files changed

+105
-10
lines changed

benchmark/buffers/buffer-copy.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use strict';
2+
const common = require('../common.js');
3+
4+
const bench = common.createBenchmark(main, {
5+
bytes: [0, 8, 128, 32 * 1024],
6+
partial: ['true', 'false'],
7+
n: [6e6]
8+
});
9+
10+
function main({ n, bytes, partial }) {
11+
const source = Buffer.allocUnsafe(bytes);
12+
const target = Buffer.allocUnsafe(bytes);
13+
const sourceStart = (partial === 'true' ? Math.floor(bytes / 2) : 0);
14+
bench.start();
15+
for (let i = 0; i < n; i++) {
16+
source.copy(target, 0, sourceStart);
17+
}
18+
bench.end(n);
19+
}

lib/buffer.js

+69-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ const { Math, Object } = primordials;
2525

2626
const {
2727
byteLengthUtf8,
28-
copy: _copy,
2928
compare: _compare,
3029
compareOffset,
3130
createFromString,
@@ -69,6 +68,7 @@ const {
6968
ERR_INVALID_ARG_VALUE,
7069
ERR_INVALID_BUFFER_SIZE,
7170
ERR_INVALID_OPT_VALUE,
71+
ERR_OUT_OF_RANGE,
7272
ERR_UNKNOWN_ENCODING
7373
},
7474
hideStackFrames
@@ -157,6 +157,74 @@ function showFlaggedDeprecation() {
157157
bufferWarningAlreadyEmitted = true;
158158
}
159159

160+
function toInteger(n, defaultVal) {
161+
n = +n;
162+
if (!Number.isNaN(n) &&
163+
n >= Number.MIN_SAFE_INTEGER &&
164+
n <= Number.MAX_SAFE_INTEGER) {
165+
return ((n % 1) === 0 ? n : Math.floor(n));
166+
}
167+
return defaultVal;
168+
}
169+
170+
function _copy(source, target, targetStart, sourceStart, sourceEnd) {
171+
if (!isUint8Array(source))
172+
throw new ERR_INVALID_ARG_TYPE('source', ['Buffer', 'Uint8Array'], source);
173+
if (!isUint8Array(target))
174+
throw new ERR_INVALID_ARG_TYPE('target', ['Buffer', 'Uint8Array'], target);
175+
176+
if (targetStart === undefined) {
177+
targetStart = 0;
178+
} else {
179+
targetStart = toInteger(targetStart, 0);
180+
if (targetStart < 0)
181+
throw new ERR_OUT_OF_RANGE('targetStart', '>= 0', targetStart);
182+
}
183+
184+
if (sourceStart === undefined) {
185+
sourceStart = 0;
186+
} else {
187+
sourceStart = toInteger(sourceStart, 0);
188+
if (sourceStart < 0)
189+
throw new ERR_OUT_OF_RANGE('sourceStart', '>= 0', sourceStart);
190+
}
191+
192+
if (sourceEnd === undefined) {
193+
sourceEnd = source.length;
194+
} else {
195+
sourceEnd = toInteger(sourceEnd, 0);
196+
if (sourceEnd < 0)
197+
throw new ERR_OUT_OF_RANGE('sourceEnd', '>= 0', sourceEnd);
198+
}
199+
200+
if (targetStart >= target.length || sourceStart >= sourceEnd)
201+
return 0;
202+
203+
if (sourceStart > source.length) {
204+
throw new ERR_OUT_OF_RANGE('sourceStart',
205+
`<= ${source.length}`,
206+
sourceStart);
207+
}
208+
209+
if (sourceEnd - sourceStart > target.length - targetStart)
210+
sourceEnd = sourceStart + target.length - targetStart;
211+
212+
let nb = sourceEnd - sourceStart;
213+
const targetLen = target.length - targetStart;
214+
const sourceLen = source.length - sourceStart;
215+
if (nb > targetLen)
216+
nb = targetLen;
217+
if (nb > sourceLen)
218+
nb = sourceLen;
219+
220+
if (sourceStart !== 0 || sourceEnd !== source.length)
221+
source = new Uint8Array(source.buffer, source.byteOffset + sourceStart, nb);
222+
223+
target.set(source, targetStart);
224+
225+
return nb;
226+
}
227+
160228
/**
161229
* The Buffer() constructor is deprecated in documentation and should not be
162230
* used moving forward. Rather, developers should use one of the three new

test/parallel/test-buffer-alloc.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,8 @@ common.expectsError(
967967
{
968968
code: 'ERR_INVALID_ARG_TYPE',
969969
type: TypeError,
970-
message: 'argument must be a buffer'
970+
message: 'The "target" argument must be one of type Buffer or Uint8Array.' +
971+
' Received type undefined'
971972
});
972973

973974
assert.throws(() => Buffer.from(), {

test/parallel/test-buffer-copy.js

+15-8
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@ const assert = require('assert');
66
const b = Buffer.allocUnsafe(1024);
77
const c = Buffer.allocUnsafe(512);
88

9-
const errorProperty = {
10-
code: 'ERR_OUT_OF_RANGE',
11-
type: RangeError,
12-
message: 'Index out of range'
13-
};
14-
159
let cntr = 0;
1610

1711
{
@@ -116,7 +110,13 @@ b.copy(c, 0, 100, 10);
116110
// Copy throws at negative sourceStart
117111
common.expectsError(
118112
() => Buffer.allocUnsafe(5).copy(Buffer.allocUnsafe(5), 0, -1),
119-
errorProperty);
113+
{
114+
code: 'ERR_OUT_OF_RANGE',
115+
type: RangeError,
116+
message: 'The value of "sourceStart" is out of range. ' +
117+
'It must be >= 0. Received -1'
118+
}
119+
);
120120

121121
{
122122
// Check sourceEnd resets to targetEnd if former is greater than the latter
@@ -130,7 +130,14 @@ common.expectsError(
130130

131131
// Throw with negative sourceEnd
132132
common.expectsError(
133-
() => b.copy(c, 0, -1), errorProperty);
133+
() => b.copy(c, 0, 0, -1),
134+
{
135+
code: 'ERR_OUT_OF_RANGE',
136+
type: RangeError,
137+
message: 'The value of "sourceEnd" is out of range. ' +
138+
'It must be >= 0. Received -1'
139+
}
140+
);
134141

135142
// When sourceStart is greater than sourceEnd, zero copied
136143
assert.strictEqual(b.copy(c, 0, 100, 10), 0);

0 commit comments

Comments
 (0)