Browse Source

util: refactor format method.Performance improved.

Method format refactored to make it more maintenable, replacing the
switch by a function factory, that returns the appropiated function
given the character (d, i , f, j, s).

Also, performance when formatting an string that contains several
consecutive % symbols is improved. The test:
`const numSamples = 10000000;
const strPercents = '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%s%%%%%%%%%%%%%%%%%i%%%%%%%%%%%%%%%%%%%%%%%%%%';
var s;
console.time('Percents');
for (let i = 0; i < numSamples; i++) {
  s = util.format(strPercents, 'test', 12);
}
console.timeEnd('Percents');`

Original time:   28399.708ms
After refactor:  23763.788ms
Improved: 16%

PR-URL: https://github.com/nodejs/node/pull/12407
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Roman Reiss <me@silverwind.io>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Brian White <mscdex@mscdex.net>
v6
Jesus Seijas 8 years ago
committed by Roman Reiss
parent
commit
41919626e5
No known key found for this signature in database GPG Key ID: 2E62B41C93869443
  1. 40
      lib/util.js
  2. 8
      test/parallel/test-util-format.js

40
lib/util.js

@ -68,63 +68,51 @@ exports.format = function(f) {
return objects.join(' '); return objects.join(' ');
} }
var argLen = arguments.length; if (arguments.length === 1) return f;
if (argLen === 1) return f;
var str = ''; var str = '';
var a = 1; var a = 1;
var lastPos = 0; var lastPos = 0;
for (var i = 0; i < f.length;) { for (var i = 0; i < f.length;) {
if (f.charCodeAt(i) === 37/*'%'*/ && i + 1 < f.length) { if (f.charCodeAt(i) === 37/*'%'*/ && i + 1 < f.length) {
if (f.charCodeAt(i + 1) !== 37/*'%'*/ && a >= arguments.length) {
++i;
continue;
}
switch (f.charCodeAt(i + 1)) { switch (f.charCodeAt(i + 1)) {
case 100: // 'd' case 100: // 'd'
if (a >= argLen)
break;
if (lastPos < i) if (lastPos < i)
str += f.slice(lastPos, i); str += f.slice(lastPos, i);
str += Number(arguments[a++]); str += Number(arguments[a++]);
lastPos = i = i + 2; break;
continue;
case 105: // 'i' case 105: // 'i'
if (a >= argLen)
break;
if (lastPos < i) if (lastPos < i)
str += f.slice(lastPos, i); str += f.slice(lastPos, i);
str += parseInt(arguments[a++]); str += parseInt(arguments[a++]);
lastPos = i = i + 2; break;
continue;
case 102: // 'f' case 102: // 'f'
if (a >= argLen)
break;
if (lastPos < i) if (lastPos < i)
str += f.slice(lastPos, i); str += f.slice(lastPos, i);
str += parseFloat(arguments[a++]); str += parseFloat(arguments[a++]);
lastPos = i = i + 2; break;
continue;
case 106: // 'j' case 106: // 'j'
if (a >= argLen)
break;
if (lastPos < i) if (lastPos < i)
str += f.slice(lastPos, i); str += f.slice(lastPos, i);
str += tryStringify(arguments[a++]); str += tryStringify(arguments[a++]);
lastPos = i = i + 2; break;
continue;
case 115: // 's' case 115: // 's'
if (a >= argLen)
break;
if (lastPos < i) if (lastPos < i)
str += f.slice(lastPos, i); str += f.slice(lastPos, i);
str += String(arguments[a++]); str += String(arguments[a++]);
lastPos = i = i + 2; break;
continue;
case 37: // '%' case 37: // '%'
if (lastPos < i) if (lastPos < i)
str += f.slice(lastPos, i); str += f.slice(lastPos, i);
str += '%'; str += '%';
lastPos = i = i + 2; break;
continue;
} }
lastPos = i = i + 2;
continue;
} }
++i; ++i;
} }
@ -132,7 +120,7 @@ exports.format = function(f) {
str = f; str = f;
else if (lastPos < f.length) else if (lastPos < f.length)
str += f.slice(lastPos); str += f.slice(lastPos);
while (a < argLen) { while (a < arguments.length) {
const x = arguments[a++]; const x = arguments[a++];
if (x === null || (typeof x !== 'object' && typeof x !== 'symbol')) { if (x === null || (typeof x !== 'object' && typeof x !== 'symbol')) {
str += ' ' + x; str += ' ' + x;

8
test/parallel/test-util-format.js

@ -106,6 +106,8 @@ assert.strictEqual(util.format('%%s%s', 'foo'), '%sfoo');
assert.strictEqual(util.format('%s:%s'), '%s:%s'); assert.strictEqual(util.format('%s:%s'), '%s:%s');
assert.strictEqual(util.format('%s:%s', undefined), 'undefined:%s'); assert.strictEqual(util.format('%s:%s', undefined), 'undefined:%s');
assert.strictEqual(util.format('%s:%s', 'foo'), 'foo:%s'); assert.strictEqual(util.format('%s:%s', 'foo'), 'foo:%s');
assert.strictEqual(util.format('%s:%i', 'foo'), 'foo:%i');
assert.strictEqual(util.format('%s:%f', 'foo'), 'foo:%f');
assert.strictEqual(util.format('%s:%s', 'foo', 'bar'), 'foo:bar'); assert.strictEqual(util.format('%s:%s', 'foo', 'bar'), 'foo:bar');
assert.strictEqual(util.format('%s:%s', 'foo', 'bar', 'baz'), 'foo:bar baz'); assert.strictEqual(util.format('%s:%s', 'foo', 'bar', 'baz'), 'foo:bar baz');
assert.strictEqual(util.format('%%%s%%', 'hi'), '%hi%'); assert.strictEqual(util.format('%%%s%%', 'hi'), '%hi%');
@ -114,6 +116,12 @@ assert.strictEqual(util.format('%sbc%%def', 'a'), 'abc%def');
assert.strictEqual(util.format('%d:%d', 12, 30), '12:30'); assert.strictEqual(util.format('%d:%d', 12, 30), '12:30');
assert.strictEqual(util.format('%d:%d', 12), '12:%d'); assert.strictEqual(util.format('%d:%d', 12), '12:%d');
assert.strictEqual(util.format('%d:%d'), '%d:%d'); assert.strictEqual(util.format('%d:%d'), '%d:%d');
assert.strictEqual(util.format('%i:%i', 12, 30), '12:30');
assert.strictEqual(util.format('%i:%i', 12), '12:%i');
assert.strictEqual(util.format('%i:%i'), '%i:%i');
assert.strictEqual(util.format('%f:%f', 12, 30), '12:30');
assert.strictEqual(util.format('%f:%f', 12), '12:%f');
assert.strictEqual(util.format('%f:%f'), '%f:%f');
assert.strictEqual(util.format('o: %j, a: %j', {}, []), 'o: {}, a: []'); assert.strictEqual(util.format('o: %j, a: %j', {}, []), 'o: {}, a: []');
assert.strictEqual(util.format('o: %j, a: %j', {}), 'o: {}, a: %j'); assert.strictEqual(util.format('o: %j, a: %j', {}), 'o: {}, a: %j');
assert.strictEqual(util.format('o: %j, a: %j'), 'o: %j, a: %j'); assert.strictEqual(util.format('o: %j, a: %j'), 'o: %j, a: %j');

Loading…
Cancel
Save