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:
parent
6a5ab5d550
commit
71097744b2
82
lib/fs.js
82
lib/fs.js
@ -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]) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user