From 9f6d1f6fc2d099c0fb2a72f1bb2cefd8943bde19 Mon Sep 17 00:00:00 2001
From: Joyee Cheung <joyeec9h3@gmail.com>
Date: Sun, 25 Dec 2016 03:32:26 +0800
Subject: [PATCH] util: improve readability of normalizeEncoding

* Improve readability of util.normalizeEncoding
  and add some comments
* Add a benchmark for util.normalizeEncoding

PR-URL: https://github.com/nodejs/node/pull/10439
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Andreas Madsen <amwebdk@gmail.com>
---
 benchmark/util/normalize-encoding.js | 65 ++++++++++++++++++++++++++++
 lib/internal/util.js                 | 14 ++++--
 2 files changed, 75 insertions(+), 4 deletions(-)
 create mode 100644 benchmark/util/normalize-encoding.js

diff --git a/benchmark/util/normalize-encoding.js b/benchmark/util/normalize-encoding.js
new file mode 100644
index 00000000000000..8c4d03478104b0
--- /dev/null
+++ b/benchmark/util/normalize-encoding.js
@@ -0,0 +1,65 @@
+'use strict';
+
+const common = require('../common.js');
+const assert = require('assert');
+
+const groupedInputs = {
+  group_common: ['undefined', 'utf8', 'utf-8', 'base64', 'binary', 'latin1'],
+  group_upper: ['UTF-8', 'UTF8', 'UCS2', 'UTF-16LE', 'UTF16LE', 'BASE64'],
+  group_uncommon: [ 'foo', '1', 'false', 'undefined', '[]'],
+  group_misc: ['', 'utf16le', 'usc2', 'hex', 'HEX', 'BINARY']
+};
+
+const inputs = [
+  '', 'utf8', 'utf-8', 'UTF-8',
+  'UTF8', 'Utf8', 'uTf-8', 'utF-8', 'ucs2',
+  'UCS2', 'utf16le', 'utf-16le', 'UTF-16LE', 'UTF16LE',
+  'binary', 'BINARY', 'latin1', 'base64', 'BASE64',
+  'hex', 'HEX', 'foo', '1', 'false', 'undefined', '[]'];
+
+const bench = common.createBenchmark(main, {
+  input: inputs.concat(Object.keys(groupedInputs)),
+  n: [1e5]
+}, {
+  flags: '--expose-internals'
+});
+
+function getInput(input) {
+  switch (input) {
+    case 'group_common':
+      return groupedInputs.group_common;
+    case 'group_upper':
+      return groupedInputs.group_upper;
+    case 'group_uncommon':
+      return groupedInputs.group_uncommon;
+    case 'group_misc':
+      return groupedInputs.group_misc;
+    case '1':
+      return [1];
+    case 'false':
+      return [false];
+    case 'undefined':
+      return [undefined];
+    case '[]':
+      return [[]];
+    default:
+      return [input];
+  }
+}
+
+function main(conf) {
+  const normalizeEncoding = require('internal/util').normalizeEncoding;
+
+  const n = conf.n | 0;
+  const inputs = getInput(conf.input);
+  var noDead = '';
+
+  bench.start();
+  for (var i = 0; i < n; i += 1) {
+    for (var j = 0; j < inputs.length; ++j) {
+      noDead = normalizeEncoding(inputs[j]);
+    }
+  }
+  bench.end(n);
+  assert.ok(noDead === undefined || noDead.length > 0);
+}
diff --git a/lib/internal/util.js b/lib/internal/util.js
index 0c7f391eb1fad1..b7facfbbbfda68 100644
--- a/lib/internal/util.js
+++ b/lib/internal/util.js
@@ -103,10 +103,16 @@ exports.assertCrypto = function(exports) {
 };
 
 exports.kIsEncodingSymbol = Symbol('node.isEncoding');
+
+// The loop should only run at most twice, retrying with lowercased enc
+// if there is no match in the first pass.
+// We use a loop instead of branching to retry with a helper
+// function in order to avoid the performance hit.
+// Return undefined if there is no match.
 exports.normalizeEncoding = function normalizeEncoding(enc) {
   if (!enc) return 'utf8';
-  var low;
-  for (;;) {
+  var retried;
+  while (true) {
     switch (enc) {
       case 'utf8':
       case 'utf-8':
@@ -124,9 +130,9 @@ exports.normalizeEncoding = function normalizeEncoding(enc) {
       case 'hex':
         return enc;
       default:
-        if (low) return; // undefined
+        if (retried) return; // undefined
         enc = ('' + enc).toLowerCase();
-        low = true;
+        retried = true;
     }
   }
 };