path: improve path.resolve() performance when used as process.cwd() · nodejs/node@b882148

8 files changed

lines changed

Original file line numberDiff line numberDiff line change

@@ -16,8 +16,8 @@ function main({ n, paths }) {

1616
1717

bench.start();

1818

for (let i = 0; i < n; i++) {

19-

if (i % 3 === 0) {

20-

copy[1] = `${orig}${i}`;

19+

if (i % 5 === 0) {

20+

copy[1] = `${orig}/${i}`;

2121

posix.join(...copy);

2222

} else {

2323

posix.join(...args);

Original file line numberDiff line numberDiff line change

@@ -16,8 +16,8 @@ function main({ n, paths }) {

1616
1717

bench.start();

1818

for (let i = 0; i < n; i++) {

19-

if (i % 3 === 0) {

20-

copy[1] = `${orig}${i}`;

19+

if (i % 5 === 0) {

20+

copy[1] = `${orig}\\${i}`;

2121

win32.join(...copy);

2222

} else {

2323

win32.join(...args);

Original file line numberDiff line numberDiff line change

@@ -17,7 +17,7 @@ const bench = common.createBenchmark(main, {

1717

function main({ n, path }) {

1818

bench.start();

1919

for (let i = 0; i < n; i++) {

20-

posix.normalize(i % 3 === 0 ? `${path}${i}` : path);

20+

posix.normalize(i % 5 === 0 ? `${path}/${i}` : path);

2121

}

2222

bench.end(n);

2323

}

Original file line numberDiff line numberDiff line change

@@ -17,7 +17,7 @@ const bench = common.createBenchmark(main, {

1717

function main({ n, path }) {

1818

bench.start();

1919

for (let i = 0; i < n; i++) {

20-

win32.normalize(i % 3 === 0 ? `${path}${i}` : path);

20+

win32.normalize(i % 5 === 0 ? `${path}\\${i}` : path);

2121

}

2222

bench.end(n);

2323

}

Original file line numberDiff line numberDiff line change

@@ -4,23 +4,26 @@ const { posix } = require('path');

44
55

const bench = common.createBenchmark(main, {

66

paths: [

7+

'empty',

78

'',

9+

'.',

810

['', ''].join('|'),

911

['foo/bar', '/tmp/file/', '..', 'a/../subfile'].join('|'),

1012

['a/b/c/', '../../..'].join('|'),

13+

['/a/b/c/', 'abc'].join('|'),

1114

],

1215

n: [1e5],

1316

});

1417
1518

function main({ n, paths }) {

16-

const args = paths.split('|');

19+

const args = paths === 'empty' ? [] : paths.split('|');

1720

const copy = [...args];

18-

const orig = copy[0];

21+

const orig = copy[0] ?? '';

1922
2023

bench.start();

2124

for (let i = 0; i < n; i++) {

22-

if (i % 3 === 0) {

23-

copy[0] = `${orig}${i}`;

25+

if (i % 5 === 0) {

26+

copy[0] = `${orig}/${i}`;

2427

posix.resolve(...copy);

2528

} else {

2629

posix.resolve(...args);

Original file line numberDiff line numberDiff line change

@@ -4,7 +4,9 @@ const { win32 } = require('path');

44
55

const bench = common.createBenchmark(main, {

66

paths: [

7+

'empty',

78

'',

9+

'.',

810

['', ''].join('|'),

911

['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'].join('|'),

1012

['c:/blah\\blah', 'd:/games', 'c:../a'].join('|'),

@@ -13,14 +15,14 @@ const bench = common.createBenchmark(main, {

1315

});

1416
1517

function main({ n, paths }) {

16-

const args = paths.split('|');

18+

const args = paths === 'empty' ? [] : paths.split('|');

1719

const copy = [...args];

18-

const orig = copy[0];

20+

const orig = copy[0] ?? '';

1921
2022

bench.start();

2123

for (let i = 0; i < n; i++) {

22-

if (i % 3 === 0) {

23-

copy[0] = `${orig}${i}`;

24+

if (i % 5 === 0) {

25+

copy[0] = `${orig}\\${i}`;

2426

win32.resolve(...copy);

2527

} else {

2628

win32.resolve(...args);

Original file line numberDiff line numberDiff line change

@@ -95,7 +95,7 @@ function normalizeString(path, allowAboveRoot, separator, isPathSeparator) {

9595

StringPrototypeCharCodeAt(res, res.length - 1) !== CHAR_DOT ||

9696

StringPrototypeCharCodeAt(res, res.length - 2) !== CHAR_DOT) {

9797

if (res.length > 2) {

98-

const lastSlashIndex = StringPrototypeLastIndexOf(res, separator);

98+

const lastSlashIndex = res.length - lastSegmentLength - 1;

9999

if (lastSlashIndex === -1) {

100100

res = '';

101101

lastSegmentLength = 0;

@@ -163,6 +163,8 @@ function _format(sep, pathObject) {

163163

return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`;

164164

}

165165
166+

const forwardSlashRegExp = /\//g;

167+
166168

const win32 = {

167169

/**

168170

* path.resolve([from ...], to)

@@ -186,6 +188,14 @@ const win32 = {

186188

}

187189

} else if (resolvedDevice.length === 0) {

188190

path = process.cwd();

191+

// Fast path for current directory

192+

if (args.length === 0 || ((args.length === 1 && (args[0] === '' || args[0] === '.')) &&

193+

isPathSeparator(StringPrototypeCharCodeAt(path, 0)))) {

194+

if (!isWindows) {

195+

path = StringPrototypeReplace(path, forwardSlashRegExp, '\\');

196+

}

197+

return path;

198+

}

189199

} else {

190200

// Windows has the concept of drive-specific current working

191201

// directories. If we've resolved a drive letter but not yet an

@@ -1171,6 +1181,12 @@ const posix = {

11711181

* @returns {string}

11721182

*/

11731183

resolve(...args) {

1184+

if (args.length === 0 || (args.length === 1 && (args[0] === '' || args[0] === '.'))) {

1185+

const cwd = posixCwd();

1186+

if (StringPrototypeCharCodeAt(cwd, 0) === CHAR_FORWARD_SLASH) {

1187+

return cwd;

1188+

}

1189+

}

11741190

let resolvedPath = '';

11751191

let resolvedAbsolute = false;

11761192
Original file line numberDiff line numberDiff line change

@@ -24,6 +24,8 @@ const resolveTests = [

2424

[['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'], 'd:\\e.exe'],

2525

[['c:/ignore', 'c:/some/file'], 'c:\\some\\file'],

2626

[['d:/ignore', 'd:some/dir//'], 'd:\\ignore\\some\\dir'],

27+

[[], process.cwd()],

28+

[[''], process.cwd()],

2729

[['.'], process.cwd()],

2830

[['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative'],

2931

[['c:/', '//'], 'c:\\'],

@@ -42,6 +44,8 @@ const resolveTests = [

4244

[[['/var/lib', '../', 'file/'], '/var/file'],

4345

[['/var/lib', '/../', 'file/'], '/file'],

4446

[['a/b/c/', '../../..'], posixyCwd],

47+

[[], posixyCwd],

48+

[[''], posixyCwd],

4549

[['.'], posixyCwd],

4650

[['/some/dir', '.', '/absolute/'], '/absolute'],

4751

[['/foo/tmp.3/', '../tmp.3/cycles/root.js'], '/foo/tmp.3/cycles/root.js'],