buf.indexOf()

Finds the first occurrence of a string, byte or Buffer inside a Buffer, or -1 if absent.

Signature
buf.indexOf(value[, byteOffset][, encoding])
  • Added in Node.js v1.5.0

Parameters

ParameterTypeDescription
$valuestring | Buffer | Uint8Array | integerWhat to search for. A number is interpreted as an unsigned 8-bit value between 0 and 255.
$byteOffsetoptionalinteger= 0Where to begin searching in buf. A negative value counts back from the end.
$encodingoptionalstring= 'utf8'The encoding of value if value is a string.

Return value

integerThe index of the first occurrence of value in buf, or -1 if buf does not contain value.

Errors

Throws ERR_INVALID_ARG_TYPE if value is not a string, Buffer, Uint8Array or number, and ERR_UNKNOWN_ENCODING for an unsupported encoding name.

Changelog

  • PHP v8.0.0The value can now be a Uint8Array.
  • PHP v5.7.0When encoding is passed, the byteOffset parameter is no longer required.

buf.indexOf() searches raw bytes, so it works on data that is not valid text and never allocates a string to do it. It is the right tool for locating a delimiter in a stream buffer or a marker inside a binary file.

The index it returns is a byte offset

Not a character offset. With multi-byte text the two differ, and the result feeds directly into subarray or toString, both of which also take byte offsets.

A number means a byte value

buf.indexOf(10) looks for the byte 0x0A, not for the two-character string '10'. Values outside 0–255 are coerced into that range, so buf.indexOf(256) searches for 0. If you meant the text, pass a string.

Encoding matters for string values

The search string is encoded with encoding (default 'utf8') before the byte comparison. Searching a UTF-16LE buffer for a UTF-8 encoded needle finds nothing, with no error to tell you why. Since v5.7.0 you may pass the encoding without a byteOffset.

Companions: buf.lastIndexOf() searches backwards and buf.includes() returns a boolean. An empty string matches at byteOffset, clamped to buf.length.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Split an accumulating stream buffer on a newline or CRLF delimiter.
  • Find the end of the headers in a raw HTTP message.
  • Locate a magic marker inside a binary file format.
  • Check whether a payload contains a boundary before parsing multipart data.

Frequently asked questions

How do I find a substring inside a Buffer in Node.js?

Call buf.indexOf('needle'), which returns the byte offset of the first match or -1. Use buf.includes for a boolean and buf.lastIndexOf to search from the end.

Why does buf.indexOf(10) not find the number 10 in my Buffer?

Because a numeric argument is treated as a byte value, so indexOf(10) searches for 0x0A, the newline byte. Pass the string '10' if you are looking for the two text characters.

How do I split a Buffer on a delimiter?

Loop on buf.indexOf(delimiter), taking buf.subarray(0, i) as the piece and reassigning buf to buf.subarray(i + delimiter.length). Buffer has no split method, and this pattern avoids decoding the data to a string first.

More examples

Line splitting across stream chunks

The incomplete trailing chunk stays in pending until the delimiter arrives, which is why 'two' only appears after the second feed. Searching the bytes rather than decoding first avoids splitting a multi-byte character at the chunk boundary.

Example · javascript
let pending = Buffer.alloc(0);

function feed(chunk) {
  pending = Buffer.concat([pending, chunk]);
  const lines = [];
  let i;
  while ((i = pending.indexOf('\n')) !== -1) {
    lines.push(pending.subarray(0, i).toString('utf8'));
    pending = pending.subarray(i + 1);   // view, not a copy
  }
  return lines;
}

console.log(feed(Buffer.from('one\ntw')));       // [ 'one' ]
console.log(feed(Buffer.from('o\nthree\n')));    // [ 'two', 'three' ]
console.log(pending.length);                     // 0

Finding the header/body split in a raw HTTP message

The blank line between headers and body is a four-byte sequence, so the body starts at sep + 4. Decoding only the header region as latin1 keeps the body bytes untouched, which matters when the body is an image rather than text.

Example · javascript
const message = Buffer.from(
  'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 5\r\n\r\nhello'
);

const sep = message.indexOf('\r\n\r\n');
console.log('separator at byte', sep);            // 63

const head = message.subarray(0, sep).toString('latin1');
const body = message.subarray(sep + 4);

console.log(head.split('\r\n')[0]);               // HTTP/1.1 200 OK
console.log(body.toString(), body.length);        // hello 5
console.log(message.indexOf('Content-Length'));   // 41

Byte values, encodings and the reverse search

A numeric needle is always a byte, wrapped into 0-255, so 0x12d finds the same '-' as 0x2d. The last pair is the failure people spend longest on: the needle is encoded before comparison, so searching UTF-16LE data with a default UTF-8 needle returns -1 rather than an error.

Example · javascript
const buf = Buffer.from('a-b-c');

console.log(buf.indexOf(0x2d));        // 1   the byte '-'
console.log(buf.indexOf('-'));         // 1   same thing as a string
console.log(buf.lastIndexOf('-'));     // 3   searching backwards
console.log(buf.indexOf(0x2d + 256));  // 1   coerced back into 0-255
console.log(buf.indexOf(''));          // 0   empty needle matches at the offset
console.log(buf.indexOf('', 99));      // 5   clamped to buf.length

// Encoding mismatches find nothing, silently.
const utf16 = Buffer.from('needle', 'utf16le');
console.log(utf16.indexOf('needle'));                // -1
console.log(utf16.indexOf('needle', 'utf16le'));     // 0

Discussion

  • Be the first to comment on this lesson.