Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions benchmark/path/matchesGlob-filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

const common = require('../common');
const assert = require('node:assert');
const { posix, win32 } = require('node:path');

const bench = common.createBenchmark(main, {
n: [2e4],
platform: ['posix', 'win32'],
pattern: ['**/node_modules/**/test/**', '**/src/**'],
});

function main({ n, platform, pattern }) {
const implementation = platform === 'posix' ? posix : win32;
const filenames = Array.from({ length: 64 }, (_, index) => {
const directory = index % 2 === 0 ? 'node_modules/dependency/test' : 'src';
return `workspace/packages/package${index}/${directory}/file${index}.js`
.replaceAll('/', implementation.sep);
});
assert.strictEqual(filenames.filter((filename) => implementation.matchesGlob(filename, pattern)).length, 32);
let matches = 0;
bench.start();
for (let iteration = 0; iteration < n; iteration++) {
for (const filename of filenames) matches += implementation.matchesGlob(filename, pattern);
}
bench.end(n * filenames.length);
assert.strictEqual(matches, n * 32);
}
31 changes: 31 additions & 0 deletions benchmark/path/matchesGlob-sections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';

const common = require('../common');
const assert = require('node:assert');
const { posix, win32 } = require('node:path');

const bench = common.createBenchmark(main, {
n: [1e5],
platform: ['posix', 'win32'],
count: [1, 2, 4, 10, 16],
result: ['hit', 'miss'],
});

function main({ n, platform, count, result }) {
const implementation = platform === 'posix' ? posix : win32;
const names = Array.from({ length: count - 1 }, (_, index) => `part${index}`);
const pattern = ['**', ...names.flatMap((name) => [name, '**']), 'file.js'].join('/');
const parts = ['root', ...names.flatMap((name) => [name, 'dir']), 'file.js'];
if (result === 'miss') {
if (names.length) parts[parts.length - 3] = 'absent';
else parts[parts.length - 1] = 'file.txt';
}
const filename = parts.join(implementation.sep);
const expected = result === 'hit';
assert.strictEqual(implementation.matchesGlob(filename, pattern), expected);
let actual;
bench.start();
for (let index = 0; index < n; index++) actual = implementation.matchesGlob(filename, pattern);
bench.end(n);
assert.strictEqual(actual, expected);
}
9 changes: 4 additions & 5 deletions src/glob/glob_matcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ class PathMatcher {
struct Section {
size_t begin, end; // pattern part indices
ptrdiff_t after; // last possible file index this section can start at
size_t non_gs;
};

Tri MatchGlobstar(Parts file,
Expand Down Expand Up @@ -660,13 +661,11 @@ class PathMatcher {
// split body into globstar-delimited sections with their last possible
// start positions
std::vector<Section> sections;
sections.push_back({body_begin, body_begin, 0});
std::vector<size_t> non_gs_sums{0};
sections.push_back({body_begin, body_begin, 0, 0});
size_t non_gs = 0;
for (size_t i = body_begin; i < body_end; i++) {
if (pattern[i].kind == PartMatcher::Kind::kGlobstar) {
non_gs_sums.push_back(non_gs);
sections.push_back({i + 1, i + 1, 0});
sections.push_back({i + 1, i + 1, 0, non_gs});
} else {
sections.back().end = i + 1;
non_gs++;
Expand All @@ -677,7 +676,7 @@ class PathMatcher {
{
size_t i = sections.size() - 1;
for (Section& s : sections) {
s.after = file_length - (static_cast<ptrdiff_t>(non_gs_sums[i--]) +
s.after = file_length - (static_cast<ptrdiff_t>(sections[i--].non_gs) +
static_cast<ptrdiff_t>(s.end - s.begin));
}
}
Expand Down
25 changes: 25 additions & 0 deletions test/parallel/test-path-glob-sections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';

require('../common');
const assert = require('node:assert');
const { posix, win32 } = require('node:path');

for (const implementation of [posix, win32]) {
for (const count of [1, 2, 4, 8, 9, 10, 16]) {
for (const prefix of ['part', '\u00e9tage', '\u4e2d']) {
const names = Array.from({ length: count - 1 }, (_, index) => `${prefix}${index}`);
const pattern = ['**', ...names.flatMap((name) => [name, '**']), 'file.js'].join('/');
const parts = ['root', ...names.flatMap((name) => [name, 'dir']), 'file.js'];
assert.strictEqual(implementation.matchesGlob(parts.join(implementation.sep), pattern), true);
if (names.length) {
parts[parts.length - 3] = 'absent';
} else {
parts[parts.length - 1] = 'file.txt';
}
assert.strictEqual(implementation.matchesGlob(parts.join(implementation.sep), pattern), false);
}
}
assert.strictEqual(implementation.matchesGlob('root/.hidden/src/lib/file.js', '**/src/**/file.js'), false);
assert.strictEqual(implementation.matchesGlob('root/src/./lib/file.js', '**/src/**/file.js'), true);
assert.strictEqual(implementation.matchesGlob('root/src/lib/file.js/', '**/src/**/file.js'), true);
}
Loading