Skip to content

Commit 7109774

Browse files
mscdexjasnell
authored andcommitted
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: #11665 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent 6a5ab5d commit 7109774

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
@@ -43,6 +43,7 @@ const internalUtil = require('internal/util');
4343
const assertEncoding = internalFS.assertEncoding;
4444
const stringToFlags = internalFS.stringToFlags;
4545
const getPathFromURL = internalURL.getPathFromURL;
46+
const { StorageObject } = require('internal/querystring');
4647

4748
Object.defineProperty(exports, 'constants', {
4849
configurable: false,
@@ -1514,10 +1515,23 @@ fs.unwatchFile = function(filename, listener) {
15141515
};
15151516

15161517

1517-
// Regex to find the device root, including trailing slash. E.g. 'c:\\'.
1518-
const splitRootRe = isWindows ?
1519-
/^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/][^\\/]+)?[\\/]*/ :
1520-
/^[/]*/;
1518+
var splitRoot;
1519+
if (isWindows) {
1520+
// Regex to find the device root on Windows (e.g. 'c:\\'), including trailing
1521+
// slash.
1522+
const splitRootRe = /^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/][^\\/]+)?[\\/]*/;
1523+
splitRoot = function splitRoot(str) {
1524+
return splitRootRe.exec(str)[0];
1525+
};
1526+
} else {
1527+
splitRoot = function splitRoot(str) {
1528+
for (var i = 0; i < str.length; ++i) {
1529+
if (str.charCodeAt(i) !== 47/*'/'*/)
1530+
return str.slice(0, i);
1531+
}
1532+
return str;
1533+
};
1534+
}
15211535

15221536
function encodeRealpathResult(result, options) {
15231537
if (!options || !options.encoding || options.encoding === 'utf8')
@@ -1545,11 +1559,17 @@ if (isWindows) {
15451559
nextPart = function nextPart(p, i) { return p.indexOf('/', i); };
15461560
}
15471561

1562+
const emptyObj = new StorageObject();
15481563
fs.realpathSync = function realpathSync(p, options) {
1549-
options = getOptions(options, {});
1550-
handleError((p = getPathFromURL(p)));
1551-
if (typeof p !== 'string')
1552-
p += '';
1564+
if (!options)
1565+
options = emptyObj;
1566+
else
1567+
options = getOptions(options, emptyObj);
1568+
if (typeof p !== 'string') {
1569+
handleError((p = getPathFromURL(p)));
1570+
if (typeof p !== 'string')
1571+
p += '';
1572+
}
15531573
nullCheck(p);
15541574
p = pathModule.resolve(p);
15551575

@@ -1559,8 +1579,8 @@ fs.realpathSync = function realpathSync(p, options) {
15591579
return maybeCachedResult;
15601580
}
15611581

1562-
const seenLinks = {};
1563-
const knownHard = {};
1582+
const seenLinks = new StorageObject();
1583+
const knownHard = new StorageObject();
15641584
const original = p;
15651585

15661586
// current character position in p
@@ -1573,10 +1593,8 @@ fs.realpathSync = function realpathSync(p, options) {
15731593
var previous;
15741594

15751595
// Skip over roots
1576-
var m = splitRootRe.exec(p);
1577-
pos = m[0].length;
1578-
current = m[0];
1579-
base = m[0];
1596+
current = base = splitRoot(p);
1597+
pos = current.length;
15801598

15811599
// On windows, check that the root exists. On unix there is no need.
15821600
if (isWindows && !knownHard[base]) {
@@ -1615,7 +1633,8 @@ fs.realpathSync = function realpathSync(p, options) {
16151633
// Use stats array directly to avoid creating an fs.Stats instance just
16161634
// for our internal use.
16171635

1618-
binding.lstat(pathModule._makeLong(base));
1636+
var baseLong = pathModule._makeLong(base);
1637+
binding.lstat(baseLong);
16191638

16201639
if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) {
16211640
knownHard[base] = true;
@@ -1631,13 +1650,13 @@ fs.realpathSync = function realpathSync(p, options) {
16311650
var dev = statValues[0/*dev*/].toString(32);
16321651
var ino = statValues[7/*ino*/].toString(32);
16331652
id = `${dev}:${ino}`;
1634-
if (seenLinks.hasOwnProperty(id)) {
1653+
if (seenLinks[id]) {
16351654
linkTarget = seenLinks[id];
16361655
}
16371656
}
16381657
if (linkTarget === null) {
1639-
binding.stat(pathModule._makeLong(base));
1640-
linkTarget = binding.readlink(pathModule._makeLong(base));
1658+
binding.stat(baseLong);
1659+
linkTarget = binding.readlink(baseLong);
16411660
}
16421661
resolvedLink = pathModule.resolve(previous, linkTarget);
16431662

@@ -1649,10 +1668,8 @@ fs.realpathSync = function realpathSync(p, options) {
16491668
p = pathModule.resolve(resolvedLink, p.slice(pos));
16501669

16511670
// Skip over roots
1652-
m = splitRootRe.exec(p);
1653-
pos = m[0].length;
1654-
current = m[0];
1655-
base = m[0];
1671+
current = base = splitRoot(p);
1672+
pos = current.length;
16561673

16571674
// On windows, check that the root exists. On unix there is no need.
16581675
if (isWindows && !knownHard[base]) {
@@ -1668,17 +1685,22 @@ fs.realpathSync = function realpathSync(p, options) {
16681685

16691686
fs.realpath = function realpath(p, options, callback) {
16701687
callback = maybeCallback(typeof options === 'function' ? options : callback);
1671-
options = getOptions(options, {});
1672-
if (handleError((p = getPathFromURL(p)), callback))
1673-
return;
1674-
if (typeof p !== 'string')
1675-
p += '';
1688+
if (!options)
1689+
options = emptyObj;
1690+
else
1691+
options = getOptions(options, emptyObj);
1692+
if (typeof p !== 'string') {
1693+
if (handleError((p = getPathFromURL(p)), callback))
1694+
return;
1695+
if (typeof p !== 'string')
1696+
p += '';
1697+
}
16761698
if (!nullCheck(p, callback))
16771699
return;
16781700
p = pathModule.resolve(p);
16791701

1680-
const seenLinks = {};
1681-
const knownHard = {};
1702+
const seenLinks = new StorageObject();
1703+
const knownHard = new StorageObject();
16821704

16831705
// current character position in p
16841706
var pos;
@@ -1689,11 +1711,8 @@ fs.realpath = function realpath(p, options, callback) {
16891711
// the partial path scanned in the previous round, with slash
16901712
var previous;
16911713

1692-
var m = splitRootRe.exec(p);
1693-
pos = m[0].length;
1694-
current = m[0];
1695-
base = m[0];
1696-
previous = '';
1714+
current = base = splitRoot(p);
1715+
pos = current.length;
16971716

16981717
// On windows, check that the root exists. On unix there is no need.
16991718
if (isWindows && !knownHard[base]) {
@@ -1756,7 +1775,7 @@ fs.realpath = function realpath(p, options, callback) {
17561775
var dev = statValues[0/*ino*/].toString(32);
17571776
var ino = statValues[7/*ino*/].toString(32);
17581777
id = `${dev}:${ino}`;
1759-
if (seenLinks.hasOwnProperty(id)) {
1778+
if (seenLinks[id]) {
17601779
return gotTarget(null, seenLinks[id], base);
17611780
}
17621781
}
@@ -1780,11 +1799,8 @@ fs.realpath = function realpath(p, options, callback) {
17801799
function gotResolvedLink(resolvedLink) {
17811800
// resolve the link, then start over
17821801
p = pathModule.resolve(resolvedLink, p.slice(pos));
1783-
var m = splitRootRe.exec(p);
1784-
pos = m[0].length;
1785-
current = m[0];
1786-
base = m[0];
1787-
previous = '';
1802+
current = base = splitRoot(p);
1803+
pos = current.length;
17881804

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

0 commit comments

Comments
 (0)