Skip to content

Commit 3f7e88a

Browse files
bnoordhuisisaacs
authored andcommitted
buffer: accept negative indices in Buffer#slice()
A negative start or end parameter now indexes from the end of the buffer. More in line with String#slice() and ArrayBuffer#slice().
1 parent 2789323 commit 3f7e88a

3 files changed

Lines changed: 36 additions & 41 deletions

File tree

doc/api/buffer.markdown

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ NOTE: Node.js v0.8 simply retained a reference to the buffer in `array.buffer`
4848
instead of cloning it.
4949

5050
While more efficient, it introduces subtle incompatibilities with the typed
51-
arrays specification. `ArrayBuffer#slice()` and `Buffer#slice()` behave
52-
differently when passed negative indices, for example.
51+
arrays specification. `ArrayBuffer#slice()` makes a copy of the slice while
52+
`Buffer#slice()` creates a view.
5353

5454
## Class: Buffer
5555

@@ -260,7 +260,7 @@ into `buf2`, starting at the 8th byte in `buf2`.
260260

261261
Returns a new buffer which references the same memory as the old, but offset
262262
and cropped by the `start` (defaults to `0`) and `end` (defaults to
263-
`buffer.length`) indexes.
263+
`buffer.length`) indexes. Negative indexes start from the end of the buffer.
264264

265265
**Modifying the new buffer slice will modify memory in the original buffer!**
266266

lib/buffer.js

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ exports.INSPECT_MAX_BYTES = 50;
2929
SlowBuffer.prototype.__proto__ = Buffer.prototype;
3030

3131

32+
function clamp(index, len, defaultValue) {
33+
if (typeof index === 'undefined') return defaultValue;
34+
index = ~~index; // Coerce to integer.
35+
if (index >= len) return len;
36+
if (index >= 0) return index;
37+
index += len;
38+
if (index >= 0) return index;
39+
return 0;
40+
}
41+
42+
3243
function toHex(n) {
3344
if (n < 16) return '0' + n.toString(16);
3445
return n.toString(16);
@@ -132,16 +143,10 @@ SlowBuffer.prototype.write = function(string, offset, length, encoding) {
132143

133144
// slice(start, end)
134145
SlowBuffer.prototype.slice = function(start, end) {
135-
if (end === undefined) end = this.length;
136-
137-
if (end > this.length) {
138-
throw new RangeError('end > this.length');
139-
}
140-
if (start > end) {
141-
throw new RangeError('start > end');
142-
}
143-
144-
return new Buffer(this, end - start, +start);
146+
var len = this.length;
147+
start = clamp(start, len, 0);
148+
end = clamp(end, len, len);
149+
return new Buffer(this, end - start, start);
145150
};
146151

147152

@@ -528,14 +533,10 @@ Buffer.prototype.copy = function(target, target_start, start, end) {
528533

529534
// slice(start, end)
530535
Buffer.prototype.slice = function(start, end) {
531-
if (end === undefined) end = this.length;
532-
if (end > this.length)
533-
throw new RangeError('end > this.length');
534-
if (start > end)
535-
throw new RangeError('start > end');
536-
if (start < 0)
537-
throw new RangeError('start < 0');
538-
return new Buffer(this.parent, end - start, +start + this.offset);
536+
var len = this.length;
537+
start = clamp(start, len, 0);
538+
end = clamp(end, len, len);
539+
return new Buffer(this.parent, end - start, start + this.offset);
539540
};
540541

541542

test/simple/test-buffer.js

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -925,30 +925,24 @@ assert.throws(function() {
925925
for (var i = 0; i < len; ++i) buf[i] = 0x42; // Try to force segfault.
926926
}, RangeError);
927927

928-
assert.throws(function() {
929-
var len = 0xfffff;
930-
var sbuf = new SlowBuffer(len);
931-
sbuf = sbuf.slice(-len); // Should throw.
932-
for (var i = 0; i < len; ++i) sbuf[i] = 0x42; // Try to force segfault.
933-
}, RangeError);
934-
935928
assert.throws(function() {
936929
var sbuf = new SlowBuffer(1);
937930
var buf = new Buffer(sbuf, 1, 0);
938931
buf.length = 0xffffffff;
939932
buf.slice(0xffffff0, 0xffffffe); // Should throw.
940933
}, Error);
941934

942-
assert.throws(function() {
943-
var sbuf = new SlowBuffer(8);
944-
var buf = new Buffer(sbuf, 8, 0);
945-
buf.slice(-8); // Should throw. Throws Error instead of RangeError
946-
// for the sake of v0.8 compatibility.
947-
}, Error);
948-
949-
assert.throws(function() {
950-
var sbuf = new SlowBuffer(16);
951-
var buf = new Buffer(sbuf, 8, 8);
952-
buf.slice(-8); // Should throw. Throws Error instead of RangeError
953-
// for the sake of v0.8 compatibility.
954-
}, Error);
935+
(function() {
936+
var buf = new Buffer('0123456789');
937+
assert.equal(buf.slice(-10, 10), '0123456789');
938+
assert.equal(buf.slice(-20, 10), '0123456789');
939+
assert.equal(buf.slice(-20, -10), '');
940+
assert.equal(buf.slice(0, -1), '012345678');
941+
assert.equal(buf.slice(2, -2), '234567');
942+
assert.equal(buf.slice(0, 65536), '0123456789');
943+
assert.equal(buf.slice(65536, 0), '');
944+
for (var i = 0, s = buf.toString(); i < buf.length; ++i) {
945+
assert.equal(buf.slice(-i), s.slice(-i));
946+
assert.equal(buf.slice(0, -i), s.slice(0, -i));
947+
}
948+
})();

0 commit comments

Comments
 (0)