From 71a0d839a7d6426650d096412b1a06e86353577f Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 1 Oct 2021 15:25:16 +0200 Subject: [PATCH] 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. --- src/wildcard.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/wildcard.cpp b/src/wildcard.cpp index 52c3646d2..2e93e44b9 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -761,6 +761,7 @@ void wildcard_expander_t::expand_intermediate_segment(const wcstring &base_dir, const wchar_t *wc_remainder, const wcstring &prefix) { wcstring name_str; + int dir_fd = dirfd(base_dir_fp); 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 .. if (!wildcard_match(name_str, wc_segment, true)) { @@ -768,9 +769,9 @@ void wildcard_expander_t::expand_intermediate_segment(const wcstring &base_dir, continue; } - wcstring full_path = base_dir + name_str; + std::string narrow = wcs2string(name_str); 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. 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 // our tail_wc, which includes the ANY_STRING_RECURSIVE guy. + wcstring full_path = base_dir + name_str; full_path.push_back(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 const scoped_push 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)) { // Don't bother with . and .. 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 match = string_fuzzy_match_string(wc_segment, name_str); if (!match || match->is_samecase_exact()) continue; - wcstring new_full_path = base_dir + name_str; - new_full_path.push_back(L'/'); + std::string narrow = wcs2string(name_str); 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 */ continue; } @@ -828,6 +833,9 @@ void wildcard_expander_t::expand_literal_intermediate_segment_with_fuzz(const wc // segment with files found through fuzzy matching. 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. const size_t before = this->resolved_completions->size(); this->expand(new_full_path, wc_remainder, child_prefix);