Skip to content
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 indexOf for empty searches #13024

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/api/buffer.md
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,10 @@ console.log(b.indexOf('b', null));
console.log(b.indexOf('b', []));
```

If `value` is an empty string or empty `Buffer` and `byteOffset` is less
than `buf.length`, `byteOffset` will be returned. If `value` is empty and
`byteOffset` is at least `buf.length`, `buf.length` will be returned.

### buf.keys()
<!-- YAML
added: v1.1.0
Expand Down Expand Up @@ -1450,6 +1454,8 @@ console.log(b.lastIndexOf('b', null));
console.log(b.lastIndexOf('b', []));
```

If `value` is an empty string or empty `Buffer`, `byteOffset` will be returned.

### buf.length
<!-- YAML
added: v0.1.90
Expand Down
2 changes: 1 addition & 1 deletion lib/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) {
// If the offset is undefined, "foo", {}, coerces to NaN, search whole buffer.
// `x !== x`-style conditionals are a faster form of `isNaN(x)`
if (byteOffset !== byteOffset) {
byteOffset = dir ? 0 : (buffer.length - 1);
byteOffset = dir ? 0 : buffer.length;
}
dir = !!dir; // Cast to bool.

Expand Down
48 changes: 35 additions & 13 deletions src/node_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -919,27 +919,29 @@ void Compare(const FunctionCallbackInfo<Value> &args) {
// Computes the offset for starting an indexOf or lastIndexOf search.
// Returns either a valid offset in [0...<length - 1>], ie inside the Buffer,
// or -1 to signal that there is no possible match.
int64_t IndexOfOffset(size_t length, int64_t offset_i64, bool is_forward) {
int64_t IndexOfOffset(size_t length,
int64_t offset_i64,
int64_t needle_length,
bool is_forward) {
int64_t length_i64 = static_cast<int64_t>(length);
if (length_i64 == 0) {
// Empty buffer, no match.
return -1;
}
if (offset_i64 < 0) {
if (offset_i64 + length_i64 >= 0) {
// Negative offsets count backwards from the end of the buffer.
return length_i64 + offset_i64;
} else if (is_forward) {
} else if (is_forward || needle_length == 0) {
// indexOf from before the start of the buffer: search the whole buffer.
return 0;
} else {
// lastIndexOf from before the start of the buffer: no match.
return -1;
}
} else {
if (offset_i64 < length_i64) {
if (offset_i64 + needle_length <= length_i64) {
// Valid positive offset.
return offset_i64;
} else if (needle_length == 0) {
// Out of buffer bounds, but empty needle: point to end of buffer.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion:
Either ref - http://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.indexof
Or state Same semantics as String.prototype.indexof

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@refack Ack, added comments (at the GetReturnValue() sites)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍
I was feeling it missing here (you explain "what" but not "why")

return length_i64;
} else if (is_forward) {
// indexOf from past the end of the buffer: no match.
return -1;
Expand Down Expand Up @@ -974,11 +976,21 @@ void IndexOfString(const FunctionCallbackInfo<Value>& args) {
const size_t needle_length =
StringBytes::Size(args.GetIsolate(), needle, enc);

if (needle_length == 0 || haystack_length == 0) {
int64_t opt_offset = IndexOfOffset(haystack_length,
offset_i64,
needle_length,
is_forward);

if (needle_length == 0) {
// Match String#indexOf() and String#lastIndexOf() behaviour.
args.GetReturnValue().Set(static_cast<double>(opt_offset));
return;
}

if (haystack_length == 0) {
return args.GetReturnValue().Set(-1);
}

int64_t opt_offset = IndexOfOffset(haystack_length, offset_i64, is_forward);
if (opt_offset <= -1) {
return args.GetReturnValue().Set(-1);
}
Expand Down Expand Up @@ -1077,11 +1089,21 @@ void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) {
const char* needle = buf_data;
const size_t needle_length = buf_length;

if (needle_length == 0 || haystack_length == 0) {
int64_t opt_offset = IndexOfOffset(haystack_length,
offset_i64,
needle_length,
is_forward);

if (needle_length == 0) {
// Match String#indexOf() and String#lastIndexOf() behaviour.
args.GetReturnValue().Set(static_cast<double>(opt_offset));
return;
}

if (haystack_length == 0) {
return args.GetReturnValue().Set(-1);
}

int64_t opt_offset = IndexOfOffset(haystack_length, offset_i64, is_forward);
if (opt_offset <= -1) {
return args.GetReturnValue().Set(-1);
}
Expand Down Expand Up @@ -1132,8 +1154,8 @@ void IndexOfNumber(const FunctionCallbackInfo<Value>& args) {
int64_t offset_i64 = args[2]->IntegerValue();
bool is_forward = args[3]->IsTrue();

int64_t opt_offset = IndexOfOffset(ts_obj_length, offset_i64, is_forward);
if (opt_offset <= -1) {
int64_t opt_offset = IndexOfOffset(ts_obj_length, offset_i64, 1, is_forward);
if (opt_offset <= -1 || ts_obj_length == 0) {
return args.GetReturnValue().Set(-1);
}
size_t offset = static_cast<size_t>(opt_offset);
Expand Down
16 changes: 8 additions & 8 deletions test/parallel/test-buffer-includes.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ assert(b.includes('bc', -Infinity));
assert(!b.includes('bc', Infinity));
assert(b.includes('f'), b.length - 1);
assert(!b.includes('z'));
assert(!b.includes(''));
assert(!b.includes('', 1));
assert(!b.includes('', b.length + 1));
assert(!b.includes('', Infinity));
assert(b.includes(''));
assert(b.includes('', 1));
assert(b.includes('', b.length + 1));
assert(b.includes('', Infinity));
assert(b.includes(buf_a));
assert(!b.includes(buf_a, 1));
assert(!b.includes(buf_a, -1));
Expand All @@ -51,10 +51,10 @@ assert(b.includes(buf_bc, -Infinity));
assert(!b.includes(buf_bc, Infinity));
assert(b.includes(buf_f), b.length - 1);
assert(!b.includes(buf_z));
assert(!b.includes(buf_empty));
assert(!b.includes(buf_empty, 1));
assert(!b.includes(buf_empty, b.length + 1));
assert(!b.includes(buf_empty, Infinity));
assert(b.includes(buf_empty));
assert(b.includes(buf_empty, 1));
assert(b.includes(buf_empty, b.length + 1));
assert(b.includes(buf_empty, Infinity));
assert(b.includes(0x61));
assert(!b.includes(0x61, 1));
assert(!b.includes(0x61, -1));
Expand Down
24 changes: 12 additions & 12 deletions test/parallel/test-buffer-indexof.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ assert.strictEqual(b.indexOf('bc', -Infinity), 1);
assert.strictEqual(b.indexOf('bc', Infinity), -1);
assert.strictEqual(b.indexOf('f'), b.length - 1);
assert.strictEqual(b.indexOf('z'), -1);
assert.strictEqual(b.indexOf(''), -1);
assert.strictEqual(b.indexOf('', 1), -1);
assert.strictEqual(b.indexOf('', b.length + 1), -1);
assert.strictEqual(b.indexOf('', Infinity), -1);
assert.strictEqual(b.indexOf(''), 0);
assert.strictEqual(b.indexOf('', 1), 1);
assert.strictEqual(b.indexOf('', b.length + 1), b.length);
assert.strictEqual(b.indexOf('', Infinity), b.length);
assert.strictEqual(b.indexOf(buf_a), 0);
assert.strictEqual(b.indexOf(buf_a, 1), -1);
assert.strictEqual(b.indexOf(buf_a, -1), -1);
Expand All @@ -53,10 +53,10 @@ assert.strictEqual(b.indexOf(buf_bc, -Infinity), 1);
assert.strictEqual(b.indexOf(buf_bc, Infinity), -1);
assert.strictEqual(b.indexOf(buf_f), b.length - 1);
assert.strictEqual(b.indexOf(buf_z), -1);
assert.strictEqual(b.indexOf(buf_empty), -1);
assert.strictEqual(b.indexOf(buf_empty, 1), -1);
assert.strictEqual(b.indexOf(buf_empty, b.length + 1), -1);
assert.strictEqual(b.indexOf(buf_empty, Infinity), -1);
assert.strictEqual(b.indexOf(buf_empty), 0);
assert.strictEqual(b.indexOf(buf_empty, 1), 1);
assert.strictEqual(b.indexOf(buf_empty, b.length + 1), b.length);
assert.strictEqual(b.indexOf(buf_empty, Infinity), b.length);
assert.strictEqual(b.indexOf(0x61), 0);
assert.strictEqual(b.indexOf(0x61, 1), -1);
assert.strictEqual(b.indexOf(0x61, -1), -1);
Expand Down Expand Up @@ -429,10 +429,10 @@ assert.strictEqual(b.lastIndexOf(buf_bc, -Infinity), -1);
assert.strictEqual(b.lastIndexOf(buf_bc, Infinity), 1);
assert.strictEqual(b.lastIndexOf(buf_f), b.length - 1);
assert.strictEqual(b.lastIndexOf(buf_z), -1);
assert.strictEqual(b.lastIndexOf(buf_empty), -1);
assert.strictEqual(b.lastIndexOf(buf_empty, 1), -1);
assert.strictEqual(b.lastIndexOf(buf_empty, b.length + 1), -1);
assert.strictEqual(b.lastIndexOf(buf_empty, Infinity), -1);
assert.strictEqual(b.lastIndexOf(buf_empty), b.length);
assert.strictEqual(b.lastIndexOf(buf_empty, 1), 1);
assert.strictEqual(b.lastIndexOf(buf_empty, b.length + 1), b.length);
assert.strictEqual(b.lastIndexOf(buf_empty, Infinity), b.length);
// lastIndexOf number:
assert.strictEqual(b.lastIndexOf(0x61), 0);
assert.strictEqual(b.lastIndexOf(0x61, 1), 0);
Expand Down