-
Notifications
You must be signed in to change notification settings - Fork 30.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
buffer: fix range checking for slowToString #2919
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -322,13 +322,31 @@ Object.defineProperty(Buffer.prototype, 'offset', { | |
function slowToString(encoding, start, end) { | ||
var loweredCase = false; | ||
|
||
start = start >>> 0; | ||
end = end === undefined || end === Infinity ? this.length : end >>> 0; | ||
if (start >= this.length || end <= 0) | ||
return ''; | ||
|
||
/* | ||
* if unsigned 32 bit value (>>> 0) of `start` is not the same as `start`, | ||
* use `0` as the default value. Similarly, if the unsigned 32 bit value of | ||
* `end` is not the same as `end`, then use the length of the buffer as the | ||
* end value. | ||
* | ||
* Note that, the comparison is done with abstract equality operator (!=) to | ||
* allow stringified numbers ('1') and Number objects (new Number(5)). | ||
*/ | ||
if (start < 0 || start >>> 0 != parseInt(+start)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if you want to, but maybe also add to comments why both parseInt('0o1101') === 0
parseInt(+'0o1101') === 577 but also that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, should be able to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but may also want to note that this is technically an int32 check, not uint32, b/c the value will be coerced to a uint32 below with |
||
start = 0; | ||
|
||
if (end > this.length || end >>> 0 != parseInt(+end)) | ||
end = this.length; | ||
|
||
start >>>= 0; | ||
end >>>= 0; | ||
|
||
if (end <= start) | ||
return ''; | ||
|
||
if (!encoding) encoding = 'utf8'; | ||
if (start < 0) start = 0; | ||
if (end > this.length) end = this.length; | ||
if (end <= start) return ''; | ||
|
||
while (true) { | ||
switch (encoding) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -172,7 +172,7 @@ inline MUST_USE_RESULT bool ParseArrayIndex(v8::Local<v8::Value> arg, | |
return true; | ||
} | ||
|
||
int32_t tmp_i = arg->Int32Value(); | ||
int32_t tmp_i = arg->Uint32Value(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. need to change |
||
|
||
if (tmp_i < 0) | ||
return false; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -248,14 +248,83 @@ b.copy(new Buffer(1), 1, 1, 1); | |
// try to copy 0 bytes from past the end of the source buffer | ||
b.copy(new Buffer(1), 0, 2048, 2048); | ||
|
||
// try to toString() a 0-length slice of a buffer, both within and without the | ||
// valid buffer range | ||
assert.equal(new Buffer('abc').toString('ascii', 0, 0), ''); | ||
assert.equal(new Buffer('abc').toString('ascii', -100, -100), ''); | ||
assert.equal(new Buffer('abc').toString('ascii', 100, 100), ''); | ||
const rangeBuffer = new Buffer('abc'); | ||
|
||
// if start >= buffer's length, empty string will be returned | ||
assert.equal(rangeBuffer.toString('ascii', 3), ''); | ||
assert.equal(rangeBuffer.toString('ascii', +Infinity), ''); | ||
assert.equal(rangeBuffer.toString('ascii', 3.14, 3), ''); | ||
assert.equal(rangeBuffer.toString('ascii', 'Infinity', 3), ''); | ||
|
||
// if end <= 0, empty string will be returned | ||
assert.equal(rangeBuffer.toString('ascii', 1, 0), ''); | ||
assert.equal(rangeBuffer.toString('ascii', 1, -1.2), ''); | ||
assert.equal(rangeBuffer.toString('ascii', 1, -100), ''); | ||
assert.equal(rangeBuffer.toString('ascii', 1, -Infinity), ''); | ||
|
||
// if start < 0, start will be taken as zero | ||
assert.equal(rangeBuffer.toString('ascii', -1, 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', -1.99, 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', -Infinity, 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', '-1', 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', '-1.99', 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', '-Infinity', 3), 'abc'); | ||
|
||
// if start is an invalid integer, start will be taken as zero | ||
assert.equal(rangeBuffer.toString('ascii', 'node.js', 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', {}, 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', [], 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', NaN, 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', null, 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', undefined, 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', false, 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', '', 3), 'abc'); | ||
|
||
// but, if start is an integer when coerced, then it will be coerced and used. | ||
assert.equal(rangeBuffer.toString('ascii', '-1', 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', '1', 3), 'bc'); | ||
assert.equal(rangeBuffer.toString('ascii', '-Infinity', 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', '3', 3), ''); | ||
assert.equal(rangeBuffer.toString('ascii', Number(3), 3), ''); | ||
assert.equal(rangeBuffer.toString('ascii', '3.14', 3), ''); | ||
assert.equal(rangeBuffer.toString('ascii', '1.99', 3), 'bc'); | ||
assert.equal(rangeBuffer.toString('ascii', '-1.99', 3), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 1.99, 3), 'bc'); | ||
assert.equal(rangeBuffer.toString('ascii', true, 3), 'bc'); | ||
|
||
// if end > buffer's length, end will be taken as buffer's length | ||
assert.equal(rangeBuffer.toString('ascii', 0, 5), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, 6.99), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, Infinity), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, '5'), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, '6.99'), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, 'Infinity'), 'abc'); | ||
|
||
// if end is an invalid integer, end will be taken as buffer's length | ||
assert.equal(rangeBuffer.toString('ascii', 0, 'node.js'), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, {}), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, NaN), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, undefined), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, null), ''); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure if these four tests returning empty strings is okay though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So should There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the end value is not valid, simply ignoring it and assuming the actual length would be logical, wouldn't it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Let's try |
||
assert.equal(rangeBuffer.toString('ascii', 0, []), ''); | ||
assert.equal(rangeBuffer.toString('ascii', 0, false), ''); | ||
assert.equal(rangeBuffer.toString('ascii', 0, ''), ''); | ||
|
||
// but, if end is an integer when coerced, then it will be coerced and used. | ||
assert.equal(rangeBuffer.toString('ascii', 0, '-1'), ''); | ||
assert.equal(rangeBuffer.toString('ascii', 0, '1'), 'a'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, '-Infinity'), ''); | ||
assert.equal(rangeBuffer.toString('ascii', 0, '3'), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, Number(3)), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, '3.14'), 'abc'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, '1.99'), 'a'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, '-1.99'), ''); | ||
assert.equal(rangeBuffer.toString('ascii', 0, 1.99), 'a'); | ||
assert.equal(rangeBuffer.toString('ascii', 0, true), 'a'); | ||
|
||
// try toString() with a object as a encoding | ||
assert.equal(new Buffer('abc').toString({toString: function() { | ||
assert.equal(rangeBuffer.toString({toString: function() { | ||
return 'ascii'; | ||
}}), 'abc'); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
end < 0
would better here instead ofend <= 0
? In order to treatfalse
asthis.length
, for example.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so all falsey values will evaluate to default value? I'd be cool with that, except we also assert that
true
is coerced to1
. seems like a small inconsistency to evaluate the two types differently.eh. guess it's instead falsey vs. truthy. okay, i'm cool with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going to revise my opinion. I think we should imitate similar behavior as v8 in other scenarios. Here's one using Typed Arrays:
So end only receives the default length iff
end === undefined
.There are cases where Typed Arrays throw where we can't, but we'll leave those aside.