Skip to content

Commit 3277fe8

Browse files
committed
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()`
1 parent 4fdfbc7 commit 3277fe8

File tree

1 file changed

+56
-40
lines changed

1 file changed

+56
-40
lines changed

lib/fs.js

+56-40
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const assertEncoding = internalFS.assertEncoding;
2222
const stringToFlags = internalFS.stringToFlags;
2323
const SyncWriteStream = internalFS.SyncWriteStream;
2424
const getPathFromURL = internalURL.getPathFromURL;
25+
const { StorageObject } = require('internal/querystring');
2526

2627
Object.defineProperty(exports, 'constants', {
2728
configurable: false,
@@ -1574,10 +1575,23 @@ fs.unwatchFile = function(filename, listener) {
15741575
};
15751576

15761577

1577-
// Regex to find the device root, including trailing slash. E.g. 'c:\\'.
1578-
const splitRootRe = isWindows ?
1579-
/^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/][^\\/]+)?[\\/]*/ :
1580-
/^[/]*/;
1578+
var splitRoot;
1579+
if (isWindows) {
1580+
// Regex to find the device root on Windows (e.g. 'c:\\'), including trailing
1581+
// slash.
1582+
const splitRootRe = /^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/][^\\/]+)?[\\/]*/;
1583+
splitRoot = function splitRoot(str) {
1584+
return splitRootRe.exec(str)[0];
1585+
};
1586+
} else {
1587+
splitRoot = function splitRoot(str) {
1588+
for (var i = 0; i < str.length; ++i) {
1589+
if (str.charCodeAt(i) !== 47/*'/'*/)
1590+
return str.slice(0, i);
1591+
}
1592+
return str;
1593+
};
1594+
}
15811595

15821596
function encodeRealpathResult(result, options) {
15831597
if (!options || !options.encoding || options.encoding === 'utf8')
@@ -1605,11 +1619,17 @@ if (isWindows) {
16051619
nextPart = function nextPart(p, i) { return p.indexOf('/', i); };
16061620
}
16071621

1622+
const emptyObj = new StorageObject();
16081623
fs.realpathSync = function realpathSync(p, options) {
1609-
options = getOptions(options, {});
1610-
handleError((p = getPathFromURL(p)));
1611-
if (typeof p !== 'string')
1612-
p += '';
1624+
if (!options)
1625+
options = emptyObj;
1626+
else
1627+
options = getOptions(options, emptyObj);
1628+
if (typeof p !== 'string') {
1629+
handleError((p = getPathFromURL(p)));
1630+
if (typeof p !== 'string')
1631+
p += '';
1632+
}
16131633
nullCheck(p);
16141634

16151635
p = pathModule.resolve(p);
@@ -1621,8 +1641,8 @@ fs.realpathSync = function realpathSync(p, options) {
16211641
return maybeCachedResult;
16221642
}
16231643

1624-
const seenLinks = {};
1625-
const knownHard = {};
1644+
const seenLinks = new StorageObject();
1645+
const knownHard = new StorageObject();
16261646
const original = p;
16271647

16281648
// current character position in p
@@ -1635,10 +1655,8 @@ fs.realpathSync = function realpathSync(p, options) {
16351655
var previous;
16361656

16371657
// Skip over roots
1638-
var m = splitRootRe.exec(p);
1639-
pos = m[0].length;
1640-
current = m[0];
1641-
base = m[0];
1658+
current = base = splitRoot(p);
1659+
pos = current.length;
16421660

16431661
// On windows, check that the root exists. On unix there is no need.
16441662
if (isWindows && !knownHard[base]) {
@@ -1677,7 +1695,8 @@ fs.realpathSync = function realpathSync(p, options) {
16771695
// Use stats array directly to avoid creating an fs.Stats instance just
16781696
// for our internal use.
16791697

1680-
binding.lstat(pathModule._makeLong(base));
1698+
var baseLong = pathModule._makeLong(base);
1699+
binding.lstat(baseLong);
16811700

16821701
if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) {
16831702
knownHard[base] = true;
@@ -1693,13 +1712,13 @@ fs.realpathSync = function realpathSync(p, options) {
16931712
var dev = statValues[0/*dev*/].toString(32);
16941713
var ino = statValues[7/*ino*/].toString(32);
16951714
id = `${dev}:${ino}`;
1696-
if (seenLinks.hasOwnProperty(id)) {
1715+
if (seenLinks[id]) {
16971716
linkTarget = seenLinks[id];
16981717
}
16991718
}
17001719
if (linkTarget === null) {
1701-
binding.stat(pathModule._makeLong(base));
1702-
linkTarget = binding.readlink(pathModule._makeLong(base));
1720+
binding.stat(baseLong);
1721+
linkTarget = binding.readlink(baseLong);
17031722
}
17041723
resolvedLink = pathModule.resolve(previous, linkTarget);
17051724

@@ -1711,10 +1730,8 @@ fs.realpathSync = function realpathSync(p, options) {
17111730
p = pathModule.resolve(resolvedLink, p.slice(pos));
17121731

17131732
// Skip over roots
1714-
m = splitRootRe.exec(p);
1715-
pos = m[0].length;
1716-
current = m[0];
1717-
base = m[0];
1733+
current = base = splitRoot(p);
1734+
pos = current.length;
17181735

17191736
// On windows, check that the root exists. On unix there is no need.
17201737
if (isWindows && !knownHard[base]) {
@@ -1730,18 +1747,23 @@ fs.realpathSync = function realpathSync(p, options) {
17301747

17311748
fs.realpath = function realpath(p, options, callback) {
17321749
callback = maybeCallback(typeof options === 'function' ? options : callback);
1733-
options = getOptions(options, {});
1734-
if (handleError((p = getPathFromURL(p)), callback))
1735-
return;
1736-
if (typeof p !== 'string')
1737-
p += '';
1750+
if (!options)
1751+
options = emptyObj;
1752+
else
1753+
options = getOptions(options, emptyObj);
1754+
if (typeof p !== 'string') {
1755+
if (handleError((p = getPathFromURL(p)), callback))
1756+
return;
1757+
if (typeof p !== 'string')
1758+
p += '';
1759+
}
17381760
if (!nullCheck(p, callback))
17391761
return;
17401762

17411763
p = pathModule.resolve(p);
17421764

1743-
const seenLinks = {};
1744-
const knownHard = {};
1765+
const seenLinks = new StorageObject();
1766+
const knownHard = new StorageObject();
17451767

17461768
// current character position in p
17471769
var pos;
@@ -1752,11 +1774,8 @@ fs.realpath = function realpath(p, options, callback) {
17521774
// the partial path scanned in the previous round, with slash
17531775
var previous;
17541776

1755-
var m = splitRootRe.exec(p);
1756-
pos = m[0].length;
1757-
current = m[0];
1758-
base = m[0];
1759-
previous = '';
1777+
current = base = splitRoot(p);
1778+
pos = current.length;
17601779

17611780
// On windows, check that the root exists. On unix there is no need.
17621781
if (isWindows && !knownHard[base]) {
@@ -1819,7 +1838,7 @@ fs.realpath = function realpath(p, options, callback) {
18191838
var dev = statValues[0/*ino*/].toString(32);
18201839
var ino = statValues[7/*ino*/].toString(32);
18211840
id = `${dev}:${ino}`;
1822-
if (seenLinks.hasOwnProperty(id)) {
1841+
if (seenLinks[id]) {
18231842
return gotTarget(null, seenLinks[id], base);
18241843
}
18251844
}
@@ -1843,11 +1862,8 @@ fs.realpath = function realpath(p, options, callback) {
18431862
function gotResolvedLink(resolvedLink) {
18441863
// resolve the link, then start over
18451864
p = pathModule.resolve(resolvedLink, p.slice(pos));
1846-
var m = splitRootRe.exec(p);
1847-
pos = m[0].length;
1848-
current = m[0];
1849-
base = m[0];
1850-
previous = '';
1865+
current = base = splitRoot(p);
1866+
pos = current.length;
18511867

18521868
// On windows, check that the root exists. On unix there is no need.
18531869
if (isWindows && !knownHard[base]) {

0 commit comments

Comments
 (0)