Permalink
Browse files

buffer: fix indexOf for empty searches

Make searches for empty subsequences do exactly what
`String.prototype.indexOf()` does.

Fixes: #13023
PR-URL: #13024
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
  • Loading branch information...
addaleax committed May 14, 2017
1 parent 5a948f6 commit 28ddac2ec285b950a17bc42d7c79c329c8573c9e
Showing with 62 additions and 34 deletions.
  1. +6 −0 doc/api/buffer.md
  2. +1 −1 lib/buffer.js
  3. +35 −13 src/node_buffer.cc
  4. +8 −8 test/parallel/test-buffer-includes.js
  5. +12 −12 test/parallel/test-buffer-indexof.js
View
@@ -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
@@ -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
View
@@ -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.
View
@@ -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;
@@ -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);
}
@@ -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);
}
@@ -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);
@@ -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));
@@ -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));
@@ -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);
@@ -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);
@@ -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);

0 comments on commit 28ddac2

Please sign in to comment.