diff --git a/benchmark/crypto/get-ciphers.js b/benchmark/crypto/get-ciphers.js new file mode 100644 index 0000000000..257c9af2fd --- /dev/null +++ b/benchmark/crypto/get-ciphers.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common.js'); + +const bench = common.createBenchmark(main, { + n: [1, 5000], + v: ['crypto', 'tls'] +}); + +function main(conf) { + const n = +conf.n; + const v = conf.v; + const method = require(v).getCiphers; + var i = 0; + + common.v8ForceOptimization(method); + bench.start(); + for (; i < n; i++) method(); + bench.end(n); +} diff --git a/lib/crypto.js b/lib/crypto.js index 4bce371701..9ffff06f7f 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -632,41 +632,23 @@ exports.randomBytes = exports.pseudoRandomBytes = randomBytes; exports.rng = exports.prng = randomBytes; -exports.getCiphers = function() { - return filterDuplicates(getCiphers()); -}; - - -exports.getHashes = function() { - return filterDuplicates(getHashes()); -}; +exports.getCiphers = internalUtil.cachedResult(() => { + return internalUtil.filterDuplicateStrings(getCiphers()); +}); +exports.getHashes = internalUtil.cachedResult(() => { + return internalUtil.filterDuplicateStrings(getHashes()); +}); -exports.getCurves = function() { - return filterDuplicates(getCurves()); -}; +exports.getCurves = internalUtil.cachedResult(() => { + return internalUtil.filterDuplicateStrings(getCurves()); +}); Object.defineProperty(exports, 'fips', { get: getFipsCrypto, set: setFipsCrypto }); -function filterDuplicates(names) { - // Drop all-caps names in favor of their lowercase aliases, - // for example, 'sha1' instead of 'SHA1'. - var ctx = {}; - names.forEach(function(name) { - var key = name; - if (/^[0-9A-Z\-]+$/.test(key)) key = key.toLowerCase(); - if (!ctx.hasOwnProperty(key) || ctx[key] < name) - ctx[key] = name; - }); - - return Object.getOwnPropertyNames(ctx).map(function(key) { - return ctx[key]; - }).sort(); -} - // Legacy API Object.defineProperty(exports, 'createCredentials', { configurable: true, diff --git a/lib/internal/util.js b/lib/internal/util.js index 8e7e51aab9..e3d9a66b3b 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -121,3 +121,34 @@ exports.normalizeEncoding = function normalizeEncoding(enc) { } } }; + +// Filters duplicate strings. Used to support functions in crypto and tls +// modules. Implemented specifically to maintain existing behaviors in each. +exports.filterDuplicateStrings = function filterDuplicateStrings(items, low) { + if (!Array.isArray(items)) + return []; + const len = items.length; + if (len <= 1) + return items; + const map = new Map(); + for (var i = 0; i < len; i++) { + const item = items[i]; + const key = item.toLowerCase(); + if (low) { + map.set(key, key); + } else { + if (!map.has(key) || map.get(key) <= item) + map.set(key, item); + } + } + return Array.from(map.values()).sort(); +}; + +exports.cachedResult = function cachedResult(fn) { + var result; + return () => { + if (result === undefined) + result = fn(); + return result; + }; +}; diff --git a/lib/tls.js b/lib/tls.js index 80ea0d7697..695edd8c5a 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -1,6 +1,7 @@ 'use strict'; -require('internal/util').assertCrypto(exports); +const internalUtil = require('internal/util'); +internalUtil.assertCrypto(exports); const net = require('net'); const url = require('url'); @@ -21,16 +22,9 @@ exports.DEFAULT_CIPHERS = exports.DEFAULT_ECDH_CURVE = 'prime256v1'; -exports.getCiphers = function() { - const names = binding.getSSLCiphers(); - // Drop all-caps names in favor of their lowercase aliases, - var ctx = {}; - names.forEach(function(name) { - if (/^[0-9A-Z\-]+$/.test(name)) name = name.toLowerCase(); - ctx[name] = true; - }); - return Object.getOwnPropertyNames(ctx).sort(); -}; +exports.getCiphers = internalUtil.cachedResult(() => { + return internalUtil.filterDuplicateStrings(binding.getSSLCiphers(), true); +}); // Convert protocols array into valid OpenSSL protocols list // ("\x06spdy/2\x08http/1.1\x08http/1.0")