From d13aba84996f576fab9c597209fb228518ed2e46 Mon Sep 17 00:00:00 2001
From: Brian White <mscdex@mscdex.net>
Date: Thu, 19 Jan 2017 22:26:18 -0500
Subject: [PATCH] buffer: improve compare() performance

PR-URL: https://github.com/nodejs/node/pull/10927
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
---
 .../buffers/buffer-compare-instance-method.js | 72 ++++++++++++++++---
 lib/buffer.js                                 | 40 ++++++-----
 2 files changed, 87 insertions(+), 25 deletions(-)

diff --git a/benchmark/buffers/buffer-compare-instance-method.js b/benchmark/buffers/buffer-compare-instance-method.js
index 0becbeee23a7d7..bb07326f3de218 100644
--- a/benchmark/buffers/buffer-compare-instance-method.js
+++ b/benchmark/buffers/buffer-compare-instance-method.js
@@ -4,26 +4,80 @@ const v8 = require('v8');
 
 const bench = common.createBenchmark(main, {
   size: [16, 512, 1024, 4096, 16386],
+  args: [1, 2, 3, 4, 5],
   millions: [1]
 });
 
 function main(conf) {
   const iter = (conf.millions >>> 0) * 1e6;
   const size = (conf.size >>> 0);
-  const b0 = new Buffer(size).fill('a');
-  const b1 = new Buffer(size).fill('a');
+  const args = (conf.args >>> 0);
+  const b0 = Buffer.alloc(size, 'a');
+  const b1 = Buffer.alloc(size, 'a');
+  const b0Len = b0.length;
+  const b1Len = b1.length;
+  var i;
 
   b1[size - 1] = 'b'.charCodeAt(0);
 
   // Force optimization before starting the benchmark
-  b0.compare(b1);
+  switch (args) {
+    case 2:
+      b0.compare(b1, 0);
+      break;
+    case 3:
+      b0.compare(b1, 0, b1Len);
+      break;
+    case 4:
+      b0.compare(b1, 0, b1Len, 0);
+      break;
+    case 5:
+      b0.compare(b1, 0, b1Len, 0, b0Len);
+      break;
+    default:
+      b0.compare(b1);
+  }
   v8.setFlagsFromString('--allow_natives_syntax');
   eval('%OptimizeFunctionOnNextCall(b0.compare)');
-  b0.compare(b1);
-
-  bench.start();
-  for (var i = 0; i < iter; i++) {
-    b0.compare(b1);
+  switch (args) {
+    case 2:
+      b0.compare(b1, 0);
+      bench.start();
+      for (i = 0; i < iter; i++) {
+        b0.compare(b1, 0);
+      }
+      bench.end(iter / 1e6);
+      break;
+    case 3:
+      b0.compare(b1, 0, b1Len);
+      bench.start();
+      for (i = 0; i < iter; i++) {
+        b0.compare(b1, 0, b1Len);
+      }
+      bench.end(iter / 1e6);
+      break;
+    case 4:
+      b0.compare(b1, 0, b1Len, 0);
+      bench.start();
+      for (i = 0; i < iter; i++) {
+        b0.compare(b1, 0, b1Len, 0);
+      }
+      bench.end(iter / 1e6);
+      break;
+    case 5:
+      b0.compare(b1, 0, b1Len, 0, b0Len);
+      bench.start();
+      for (i = 0; i < iter; i++) {
+        b0.compare(b1, 0, b1Len, 0, b0Len);
+      }
+      bench.end(iter / 1e6);
+      break;
+    default:
+      b0.compare(b1);
+      bench.start();
+      for (i = 0; i < iter; i++) {
+        b0.compare(b1);
+      }
+      bench.end(iter / 1e6);
   }
-  bench.end(iter / 1e6);
 }
diff --git a/lib/buffer.js b/lib/buffer.js
index fa8d1c61d4a309..299b9bc01177be 100644
--- a/lib/buffer.js
+++ b/lib/buffer.js
@@ -2,6 +2,7 @@
 'use strict';
 
 const binding = process.binding('buffer');
+const { compare: compare_, compareOffset } = binding;
 const { isArrayBuffer, isSharedArrayBuffer } = process.binding('util');
 const bindingObj = {};
 const internalUtil = require('internal/util');
@@ -537,36 +538,43 @@ Buffer.prototype.compare = function compare(target,
 
   if (!(target instanceof Buffer))
     throw new TypeError('Argument must be a Buffer');
+  if (arguments.length === 1)
+    return compare_(this, target);
 
   if (start === undefined)
     start = 0;
+  else if (start < 0)
+    throw new RangeError('out of range index');
+  else
+    start >>>= 0;
+
   if (end === undefined)
     end = target.length;
+  else if (end > target.length)
+    throw new RangeError('out of range index');
+  else
+    end >>>= 0;
+
   if (thisStart === undefined)
     thisStart = 0;
+  else if (thisStart < 0)
+    throw new RangeError('out of range index');
+  else
+    thisStart >>>= 0;
+
   if (thisEnd === undefined)
     thisEnd = this.length;
-
-  if (start < 0 ||
-      end > target.length ||
-      thisStart < 0 ||
-      thisEnd > this.length) {
+  else if (thisEnd > this.length)
     throw new RangeError('out of range index');
-  }
+  else
+    thisEnd >>>= 0;
 
-  if (thisStart >= thisEnd && start >= end)
-    return 0;
   if (thisStart >= thisEnd)
-    return -1;
-  if (start >= end)
+    return (start >= end ? 0 : -1);
+  else if (start >= end)
     return 1;
 
-  start >>>= 0;
-  end >>>= 0;
-  thisStart >>>= 0;
-  thisEnd >>>= 0;
-
-  return binding.compareOffset(this, target, start, thisStart, end, thisEnd);
+  return compareOffset(this, target, start, thisStart, end, thisEnd);
 };