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

doc: clarify Buffer.indexOf() and lastIndexOf() #10162

Closed
wants to merge 4 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
53 changes: 53 additions & 0 deletions doc/api/buffer.md
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,30 @@ console.log(utf16Buffer.indexOf('\u03a3', 0, 'ucs2'));
console.log(utf16Buffer.indexOf('\u03a3', -4, 'ucs2'));
```

If `value` is not a string, number, or `Buffer`, this method will throw a
`TypeError`. If `value` is a number, it will be coerced to a valid byte value,
an integer between 0 and 255.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: should we include both inclusive or something like that?


If `byteOffset` is not a number, it will be coerced to a number. Any arguments
that coerce to `NaN` or 0, like `{}`, `[]`, `null` or `undefined`, will search
the whole buffer. This behavior matches [`String#indexOf()`].

```js
const b = Buffer.from('abcdef');

// Passing a value that's a number, but not a valid byte
// Prints: 2, equivalent to searching for 99 or 'c'
console.log(b.indexOf(99.9));
console.log(b.indexOf(256 + 99));

// Passing a byteOffset that coerces to NaN or 0
// Prints: 1, searching the whole buffer
console.log(b.indexOf('b', undefined));
console.log(b.indexOf('b', {}));
console.log(b.indexOf('b', null));
console.log(b.indexOf('b', []));
```

### buf.includes(value[, byteOffset][, encoding])
<!-- YAML
added: v5.3.0
Expand Down Expand Up @@ -1284,6 +1308,33 @@ console.log(utf16Buffer.lastIndexOf('\u03a3', undefined, 'ucs2'));
console.log(utf16Buffer.lastIndexOf('\u03a3', -5, 'ucs2'));
```

If `value` is not a string, number, or `Buffer`, this method will throw a
`TypeError`. If `value` is a number, it will be coerced to a valid byte value,
an integer between 0 and 255.

If `byteOffset` is not a number, it will be coerced to a number. Any arguments
that coerce to `NaN`, like `{}` or `undefined`, will search the whole buffer.
This behavior matches [`String#lastIndexOf()`].

```js
const b = Buffer.from('abcdef');

// Passing a value that's a number, but not a valid byte
// Prints: 2, equivalent to searching for 99 or 'c'
console.log(b.lastIndexOf(99.9));
console.log(b.lastIndexOf(256 + 99));

// Passing a byteOffset that coerces to NaN
// Prints: 1, searching the whole buffer
console.log(b.lastIndexOf('b', undefined));
console.log(b.lastIndexOf('b', {}));

// Passing a byteOffset that coerces to 0
// Prints: -1, equivalent to passing 0
console.log(b.lastIndexOf('b', null));
console.log(b.lastIndexOf('b', []));
```

### buf.length
<!-- YAML
added: v0.1.90
Expand Down Expand Up @@ -2463,6 +2514,8 @@ console.log(buf);
[RFC1345]: https://tools.ietf.org/html/rfc1345
[RFC4648, Section 5]: https://tools.ietf.org/html/rfc4648#section-5
[`String.prototype.length`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length
[`String#indexOf()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf
[`String#lastIndexOf()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf
[`TypedArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
[`TypedArray.from()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from
[`Uint32Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array
Expand Down
7 changes: 4 additions & 3 deletions lib/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -598,9 +598,10 @@ function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) {
} else if (byteOffset < -0x80000000) {
byteOffset = -0x80000000;
}
byteOffset = +byteOffset; // Coerce to Number.
if (isNaN(byteOffset)) {
// If the offset is undefined, null, NaN, "foo", etc, search whole buffer.
// Coerce to Number. Values like null and [] become 0.
byteOffset = +byteOffset;
// If the offset is undefined, "foo", {}, coerces to NaN, search whole buffer.
if (Number.isNaN(byteOffset)) {
byteOffset = dir ? 0 : (buffer.length - 1);
}
dir = !!dir; // Cast to bool.
Expand Down
63 changes: 60 additions & 3 deletions test/parallel/test-buffer-indexof.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const buf_f = Buffer.from('f');
const buf_z = Buffer.from('z');
const buf_empty = Buffer.from('');

const s = 'abcdef';

assert.strictEqual(b.indexOf('a'), 0);
assert.strictEqual(b.indexOf('a', 1), -1);
assert.strictEqual(b.indexOf('a', -1), -1);
Expand Down Expand Up @@ -359,6 +361,37 @@ assert.throws(() => {
b.indexOf([]);
}, argumentExpected);

// Test weird offset arguments.
// The following offsets coerce to NaN or 0, searching the whole Buffer
assert.strictEqual(b.indexOf('b', undefined), 1);
assert.strictEqual(b.indexOf('b', {}), 1);
assert.strictEqual(b.indexOf('b', 0), 1);
assert.strictEqual(b.indexOf('b', null), 1);
assert.strictEqual(b.indexOf('b', []), 1);

// The following offset coerces to 2, in other words +[2] === 2
assert.strictEqual(b.indexOf('b', [2]), -1);

// Behavior should match String.indexOf()
assert.strictEqual(
b.indexOf('b', undefined),
s.indexOf('b', undefined));
assert.strictEqual(
b.indexOf('b', {}),
s.indexOf('b', {}));
assert.strictEqual(
b.indexOf('b', 0),
s.indexOf('b', 0));
assert.strictEqual(
b.indexOf('b', null),
s.indexOf('b', null));
assert.strictEqual(
b.indexOf('b', []),
s.indexOf('b', []));
assert.strictEqual(
b.indexOf('b', [2]),
s.indexOf('b', [2]));

// All code for handling encodings is shared between Buffer.indexOf and
// Buffer.lastIndexOf, so only testing the separate lastIndexOf semantics.

Expand Down Expand Up @@ -413,14 +446,38 @@ assert.strictEqual(b.lastIndexOf(0x61, Infinity), 0);
assert.strictEqual(b.lastIndexOf(0x0), -1);

// Test weird offset arguments.
// Behaviour should match String.lastIndexOf:
assert.strictEqual(b.lastIndexOf('b', 0), -1);
// The following offsets coerce to NaN, searching the whole Buffer
assert.strictEqual(b.lastIndexOf('b', undefined), 1);
assert.strictEqual(b.lastIndexOf('b', null), -1);
assert.strictEqual(b.lastIndexOf('b', {}), 1);

// The following offsets coerce to 0
assert.strictEqual(b.lastIndexOf('b', 0), -1);
assert.strictEqual(b.lastIndexOf('b', null), -1);
assert.strictEqual(b.lastIndexOf('b', []), -1);

// The following offset coerces to 2, in other words +[2] === 2
assert.strictEqual(b.lastIndexOf('b', [2]), 1);

// Behavior should match String.lastIndexOf()
assert.strictEqual(
b.lastIndexOf('b', undefined),
s.lastIndexOf('b', undefined));
assert.strictEqual(
b.lastIndexOf('b', {}),
s.lastIndexOf('b', {}));
assert.strictEqual(
b.lastIndexOf('b', 0),
s.lastIndexOf('b', 0));
assert.strictEqual(
b.lastIndexOf('b', null),
s.lastIndexOf('b', null));
assert.strictEqual(
b.lastIndexOf('b', []),
s.lastIndexOf('b', []));
assert.strictEqual(
b.lastIndexOf('b', [2]),
s.lastIndexOf('b', [2]));

// Test needles longer than the haystack.
assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'ucs2'), -1);
assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'utf8'), -1);
Expand Down