fs: more realpath*() optimizations

Including:

* Skip URL instance check for common (string) cases

* Avoid regexp on non-Windows platforms when parsing the root of a path

* Skip call to `getOptions()` in common case where no `options` is passed

* Avoid `hasOwnProperty()`

PR-URL: https://github.com/nodejs/node/pull/11665
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Brian White 2017-03-11 23:59:37 -05:00 committed by James M Snell
parent 6a5ab5d550
commit 71097744b2

View File

@ -43,6 +43,7 @@ const internalUtil = require('internal/util');
const assertEncoding = internalFS.assertEncoding; const assertEncoding = internalFS.assertEncoding;
const stringToFlags = internalFS.stringToFlags; const stringToFlags = internalFS.stringToFlags;
const getPathFromURL = internalURL.getPathFromURL; const getPathFromURL = internalURL.getPathFromURL;
const { StorageObject } = require('internal/querystring');
Object.defineProperty(exports, 'constants', { Object.defineProperty(exports, 'constants', {
configurable: false, configurable: false,
@ -1514,10 +1515,23 @@ fs.unwatchFile = function(filename, listener) {
}; };
// Regex to find the device root, including trailing slash. E.g. 'c:\\'. var splitRoot;
const splitRootRe = isWindows ? if (isWindows) {
/^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/][^\\/]+)?[\\/]*/ : // Regex to find the device root on Windows (e.g. 'c:\\'), including trailing
/^[/]*/; // slash.
const splitRootRe = /^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/][^\\/]+)?[\\/]*/;
splitRoot = function splitRoot(str) {
return splitRootRe.exec(str)[0];
};
} else {
splitRoot = function splitRoot(str) {
for (var i = 0; i < str.length; ++i) {
if (str.charCodeAt(i) !== 47/*'/'*/)
return str.slice(0, i);
}
return str;
};
}
function encodeRealpathResult(result, options) { function encodeRealpathResult(result, options) {
if (!options || !options.encoding || options.encoding === 'utf8') if (!options || !options.encoding || options.encoding === 'utf8')
@ -1545,11 +1559,17 @@ if (isWindows) {
nextPart = function nextPart(p, i) { return p.indexOf('/', i); }; nextPart = function nextPart(p, i) { return p.indexOf('/', i); };
} }
const emptyObj = new StorageObject();
fs.realpathSync = function realpathSync(p, options) { fs.realpathSync = function realpathSync(p, options) {
options = getOptions(options, {}); if (!options)
options = emptyObj;
else
options = getOptions(options, emptyObj);
if (typeof p !== 'string') {
handleError((p = getPathFromURL(p))); handleError((p = getPathFromURL(p)));
if (typeof p !== 'string') if (typeof p !== 'string')
p += ''; p += '';
}
nullCheck(p); nullCheck(p);
p = pathModule.resolve(p); p = pathModule.resolve(p);
@ -1559,8 +1579,8 @@ fs.realpathSync = function realpathSync(p, options) {
return maybeCachedResult; return maybeCachedResult;
} }
const seenLinks = {}; const seenLinks = new StorageObject();
const knownHard = {}; const knownHard = new StorageObject();
const original = p; const original = p;
// current character position in p // current character position in p
@ -1573,10 +1593,8 @@ fs.realpathSync = function realpathSync(p, options) {
var previous; var previous;
// Skip over roots // Skip over roots
var m = splitRootRe.exec(p); current = base = splitRoot(p);
pos = m[0].length; pos = current.length;
current = m[0];
base = m[0];
// On windows, check that the root exists. On unix there is no need. // On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) { if (isWindows && !knownHard[base]) {
@ -1615,7 +1633,8 @@ fs.realpathSync = function realpathSync(p, options) {
// Use stats array directly to avoid creating an fs.Stats instance just // Use stats array directly to avoid creating an fs.Stats instance just
// for our internal use. // for our internal use.
binding.lstat(pathModule._makeLong(base)); var baseLong = pathModule._makeLong(base);
binding.lstat(baseLong);
if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) { if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) {
knownHard[base] = true; knownHard[base] = true;
@ -1631,13 +1650,13 @@ fs.realpathSync = function realpathSync(p, options) {
var dev = statValues[0/*dev*/].toString(32); var dev = statValues[0/*dev*/].toString(32);
var ino = statValues[7/*ino*/].toString(32); var ino = statValues[7/*ino*/].toString(32);
id = `${dev}:${ino}`; id = `${dev}:${ino}`;
if (seenLinks.hasOwnProperty(id)) { if (seenLinks[id]) {
linkTarget = seenLinks[id]; linkTarget = seenLinks[id];
} }
} }
if (linkTarget === null) { if (linkTarget === null) {
binding.stat(pathModule._makeLong(base)); binding.stat(baseLong);
linkTarget = binding.readlink(pathModule._makeLong(base)); linkTarget = binding.readlink(baseLong);
} }
resolvedLink = pathModule.resolve(previous, linkTarget); resolvedLink = pathModule.resolve(previous, linkTarget);
@ -1649,10 +1668,8 @@ fs.realpathSync = function realpathSync(p, options) {
p = pathModule.resolve(resolvedLink, p.slice(pos)); p = pathModule.resolve(resolvedLink, p.slice(pos));
// Skip over roots // Skip over roots
m = splitRootRe.exec(p); current = base = splitRoot(p);
pos = m[0].length; pos = current.length;
current = m[0];
base = m[0];
// On windows, check that the root exists. On unix there is no need. // On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) { if (isWindows && !knownHard[base]) {
@ -1668,17 +1685,22 @@ fs.realpathSync = function realpathSync(p, options) {
fs.realpath = function realpath(p, options, callback) { fs.realpath = function realpath(p, options, callback) {
callback = maybeCallback(typeof options === 'function' ? options : callback); callback = maybeCallback(typeof options === 'function' ? options : callback);
options = getOptions(options, {}); if (!options)
options = emptyObj;
else
options = getOptions(options, emptyObj);
if (typeof p !== 'string') {
if (handleError((p = getPathFromURL(p)), callback)) if (handleError((p = getPathFromURL(p)), callback))
return; return;
if (typeof p !== 'string') if (typeof p !== 'string')
p += ''; p += '';
}
if (!nullCheck(p, callback)) if (!nullCheck(p, callback))
return; return;
p = pathModule.resolve(p); p = pathModule.resolve(p);
const seenLinks = {}; const seenLinks = new StorageObject();
const knownHard = {}; const knownHard = new StorageObject();
// current character position in p // current character position in p
var pos; var pos;
@ -1689,11 +1711,8 @@ fs.realpath = function realpath(p, options, callback) {
// the partial path scanned in the previous round, with slash // the partial path scanned in the previous round, with slash
var previous; var previous;
var m = splitRootRe.exec(p); current = base = splitRoot(p);
pos = m[0].length; pos = current.length;
current = m[0];
base = m[0];
previous = '';
// On windows, check that the root exists. On unix there is no need. // On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) { if (isWindows && !knownHard[base]) {
@ -1756,7 +1775,7 @@ fs.realpath = function realpath(p, options, callback) {
var dev = statValues[0/*ino*/].toString(32); var dev = statValues[0/*ino*/].toString(32);
var ino = statValues[7/*ino*/].toString(32); var ino = statValues[7/*ino*/].toString(32);
id = `${dev}:${ino}`; id = `${dev}:${ino}`;
if (seenLinks.hasOwnProperty(id)) { if (seenLinks[id]) {
return gotTarget(null, seenLinks[id], base); return gotTarget(null, seenLinks[id], base);
} }
} }
@ -1780,11 +1799,8 @@ fs.realpath = function realpath(p, options, callback) {
function gotResolvedLink(resolvedLink) { function gotResolvedLink(resolvedLink) {
// resolve the link, then start over // resolve the link, then start over
p = pathModule.resolve(resolvedLink, p.slice(pos)); p = pathModule.resolve(resolvedLink, p.slice(pos));
var m = splitRootRe.exec(p); current = base = splitRoot(p);
pos = m[0].length; pos = current.length;
current = m[0];
base = m[0];
previous = '';
// On windows, check that the root exists. On unix there is no need. // On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) { if (isWindows && !knownHard[base]) {