wildcard: Use fstatat

This allows us to skip re-wcs2stringing the base_dir again and again
by simply using the fd. It's about 10% faster in my testing.

fstatat is defined by POSIX, so it should be available everywhere.
This commit is contained in:
Fabian Homborg 2021-10-01 15:25:16 +02:00
parent 54369ba61b
commit 71a0d839a7

View File

@ -761,6 +761,7 @@ void wildcard_expander_t::expand_intermediate_segment(const wcstring &base_dir,
const wchar_t *wc_remainder, const wchar_t *wc_remainder,
const wcstring &prefix) { const wcstring &prefix) {
wcstring name_str; wcstring name_str;
int dir_fd = dirfd(base_dir_fp);
while (!interrupted_or_overflowed() && wreaddir_for_dirs(base_dir_fp, &name_str)) { while (!interrupted_or_overflowed() && wreaddir_for_dirs(base_dir_fp, &name_str)) {
// Note that it's critical we ignore leading dots here, else we may descend into . and .. // Note that it's critical we ignore leading dots here, else we may descend into . and ..
if (!wildcard_match(name_str, wc_segment, true)) { if (!wildcard_match(name_str, wc_segment, true)) {
@ -768,9 +769,9 @@ void wildcard_expander_t::expand_intermediate_segment(const wcstring &base_dir,
continue; continue;
} }
wcstring full_path = base_dir + name_str; std::string narrow = wcs2string(name_str);
struct stat buf; struct stat buf;
if (0 != wstat(full_path, &buf) || !S_ISDIR(buf.st_mode)) { if (0 != fstatat(dir_fd, narrow.c_str(), &buf, 0) || !S_ISDIR(buf.st_mode)) {
// We either can't stat it, or we did but it's not a directory. // We either can't stat it, or we did but it's not a directory.
continue; continue;
} }
@ -783,6 +784,7 @@ void wildcard_expander_t::expand_intermediate_segment(const wcstring &base_dir,
// We made it through. Perform normal wildcard expansion on this new directory, starting at // We made it through. Perform normal wildcard expansion on this new directory, starting at
// our tail_wc, which includes the ANY_STRING_RECURSIVE guy. // our tail_wc, which includes the ANY_STRING_RECURSIVE guy.
wcstring full_path = base_dir + name_str;
full_path.push_back(L'/'); full_path.push_back(L'/');
this->expand(full_path, wc_remainder, prefix + wc_segment + L'/'); this->expand(full_path, wc_remainder, prefix + wc_segment + L'/');
@ -803,6 +805,7 @@ void wildcard_expander_t::expand_literal_intermediate_segment_with_fuzz(const wc
// Mark that we are fuzzy for the duration of this function // Mark that we are fuzzy for the duration of this function
const scoped_push<bool> scoped_fuzzy(&this->has_fuzzy_ancestor, true); const scoped_push<bool> scoped_fuzzy(&this->has_fuzzy_ancestor, true);
int dir_fd = dirfd(base_dir_fp);
while (!interrupted_or_overflowed() && wreaddir_for_dirs(base_dir_fp, &name_str)) { while (!interrupted_or_overflowed() && wreaddir_for_dirs(base_dir_fp, &name_str)) {
// Don't bother with . and .. // Don't bother with . and ..
if (name_str == L"." || name_str == L"..") { if (name_str == L"." || name_str == L"..") {
@ -814,10 +817,12 @@ void wildcard_expander_t::expand_literal_intermediate_segment_with_fuzz(const wc
const maybe_t<string_fuzzy_match_t> match = string_fuzzy_match_string(wc_segment, name_str); const maybe_t<string_fuzzy_match_t> match = string_fuzzy_match_string(wc_segment, name_str);
if (!match || match->is_samecase_exact()) continue; if (!match || match->is_samecase_exact()) continue;
wcstring new_full_path = base_dir + name_str; std::string narrow = wcs2string(name_str);
new_full_path.push_back(L'/');
struct stat buf; struct stat buf;
if (0 != wstat(new_full_path, &buf) || !S_ISDIR(buf.st_mode)) { // Because we receive the path from readdir, we can assume it's
// not an absolute path, and because we skip ".." above
// we know it's not above the dir.
if (0 != fstatat(dir_fd, narrow.c_str(), &buf, 0) || !S_ISDIR(buf.st_mode)) {
/* We either can't stat it, or we did but it's not a directory */ /* We either can't stat it, or we did but it's not a directory */
continue; continue;
} }
@ -828,6 +833,9 @@ void wildcard_expander_t::expand_literal_intermediate_segment_with_fuzz(const wc
// segment with files found through fuzzy matching. // segment with files found through fuzzy matching.
const wcstring child_prefix = prefix + name_str + L'/'; const wcstring child_prefix = prefix + name_str + L'/';
wcstring new_full_path = base_dir + name_str;
new_full_path.push_back(L'/');
// Ok, this directory matches. Recurse to it. Then mark each resulting completion as fuzzy. // Ok, this directory matches. Recurse to it. Then mark each resulting completion as fuzzy.
const size_t before = this->resolved_completions->size(); const size_t before = this->resolved_completions->size();
this->expand(new_full_path, wc_remainder, child_prefix); this->expand(new_full_path, wc_remainder, child_prefix);