Skip to content

Commit

Permalink
buffer: fix indexOf for empty searches
Browse files Browse the repository at this point in the history
Make searches for empty subsequences do exactly what
`String.prototype.indexOf()` does.

Fixes: #13023
PR-URL: #13024
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Refael Ackermann <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Sakthipriyan Vairamani <[email protected]>
  • Loading branch information
addaleax committed May 18, 2017
1 parent 5a948f6 commit 28ddac2
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 34 deletions.
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.
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

0 comments on commit 28ddac2

Please sign in to comment.