Buffer.compare()

Compares two Buffers byte by byte and returns -1, 0 or 1 for use in sorting.

Signature
Buffer.compare(buf1, buf2)
  • Added in Node.js v0.11.13

Parameters

ParameterTypeDescription
$buf1Buffer | Uint8ArrayThe first value to compare.
$buf2Buffer | Uint8ArrayThe second value to compare.

Return value

integerEither -1, 0, or 1, depending on the result of the comparison: -1 when buf1 sorts before buf2, 0 when they are identical, 1 when buf1 sorts after buf2.

Errors

Throws ERR_INVALID_ARG_TYPE if either argument is not a Buffer or Uint8Array.

Changelog

  • PHP v8.0.0The arguments can now be Uint8Arrays, not only Buffers.

Buffer.compare() orders two buffers by their bytes, comparing position by position until they differ. Because it returns exactly -1, 0 or 1, it can be handed straight to Array.prototype.sort() with no wrapper.

How the ordering works

Bytes are compared as unsigned values, 0 to 255. If one buffer is a prefix of the other, the shorter one sorts first: Buffer.from('ab') comes before Buffer.from('abc'). For ASCII text this matches a plain string sort, but for anything else it does not — byte order is not locale-aware, and 'Z' (0x5A) sorts before 'a' (0x61).

Static versus instance

buf1.compare(buf2) does the same thing, and the instance form additionally accepts targetStart, targetEnd, sourceStart and sourceEnd so you can compare regions without slicing. For a plain equality test, buf1.equals(buf2) is clearer than checking for 0.

Not for secrets

Comparison stops at the first differing byte, so its running time leaks where two values diverge. When comparing tokens, signatures or MACs, use crypto.timingSafeEqual() instead.

Example

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

When to use it

  • Sort an array of binary keys or hashes into a deterministic order.
  • Order records by a binary sort key read from a database or index.
  • Deduplicate a list of buffers after sorting them.
  • Assert two buffers are identical in a test.

Frequently asked questions

How do I check if two Buffers are equal in Node.js?

Use buf1.equals(buf2), which returns a boolean, or check Buffer.compare(buf1, buf2) === 0. For secrets such as tokens or HMACs use crypto.timingSafeEqual instead, because both of these return early.

Can I use Buffer.compare directly as a sort comparator?

Yes. It returns -1, 0 or 1, which is exactly the contract Array.prototype.sort expects, so arr.sort(Buffer.compare) works with no wrapper function.

How does Buffer.compare order buffers of different lengths?

It compares bytes until one differs; if the shorter buffer is a prefix of the longer one, the shorter sorts first. Length is only the tie-breaker, never the primary key.

More examples

Sorting and deduplicating binary keys

Sorting first makes duplicates adjacent, so a single linear pass removes them without a hash map. The second block is the trap: uppercase letters have lower byte values, so 'Banana' sorts ahead of 'apple' — for human-facing ordering use Intl.Collator on the decoded strings instead.

Example · javascript
const keys = ['delta', 'alpha', 'charlie', 'alpha', 'bravo']
  .map((s) => Buffer.from(s));

keys.sort(Buffer.compare);

const unique = keys.filter((key, i) => i === 0 || Buffer.compare(key, keys[i - 1]) !== 0);
console.log(unique.map((k) => k.toString()));
// [ 'alpha', 'bravo', 'charlie', 'delta' ]

// Byte order is not alphabetical order for mixed case:
const mixed = ['apple', 'Banana', 'cherry'].map((s) => Buffer.from(s));
console.log(mixed.sort(Buffer.compare).map((b) => b.toString()));
// [ 'Banana', 'apple', 'cherry' ]

Instance compare with ranges, and equals

The four-argument instance form compares sub-ranges directly, which avoids the subarray allocations you would otherwise need inside a parsing loop. buf.equals is the right call when you only care whether the bytes match.

Example · javascript
const a = Buffer.from('ABCDEFGH');
const b = Buffer.from('..CDEF..');

// Compare a[2..6) against b[2..6) without allocating slices.
console.log(a.compare(b, 2, 6, 2, 6));   // 0
console.log(a.compare(b));               // 1  (whole buffers differ)

// Plain equality reads better than checking for 0.
console.log(a.equals(Buffer.from('ABCDEFGH')));  // true
console.log(a.equals(b));                        // false

// Uint8Arrays work as arguments since v8.
console.log(Buffer.compare(Buffer.from([1]), new Uint8Array([2]))); // -1

Why not to use it on secrets

timingSafeEqual always examines every byte, so an attacker cannot narrow a token down one byte at a time by measuring response latency. It throws when the lengths differ, which is why the length check comes first and short-circuits.

Example · javascript
const crypto = require('node:crypto');

const expected = crypto.createHmac('sha256', 'k').update('payload').digest();
const provided = Buffer.from(process.argv[2] ?? '', 'hex');

// WRONG: returns early at the first differing byte, leaking a timing signal.
// if (Buffer.compare(expected, provided) === 0) { ... }

// RIGHT: constant time, but the lengths must match first.
const ok = provided.length === expected.length &&
  crypto.timingSafeEqual(expected, provided);

console.log('signature valid:', ok);

Discussion

  • Be the first to comment on this lesson.