New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faster extension ignores #586

Merged
merged 9 commits into from Jan 29, 2015
Copy path View file
@@ -37,12 +37,14 @@ const char *ignore_pattern_files[] = {
NULL
};

int is_empty(ignores* ig) {
return (ig->names_len + ig->slash_names_len + ig->regexes_len + ig->slash_regexes_len == 0);
int is_empty(ignores *ig) {
return (ig->extensions_len + ig->names_len + ig->slash_names_len + ig->regexes_len + ig->slash_regexes_len == 0);
};

ignores *init_ignore(ignores *parent, const char *dirname, const size_t dirname_len) {
ignores *ig = ag_malloc(sizeof(ignores));
ig->extensions = NULL;
ig->extensions_len = 0;
ig->names = NULL;
ig->names_len = 0;
ig->slash_names = NULL;
@@ -75,26 +77,18 @@ ignores *init_ignore(ignores *parent, const char *dirname, const size_t dirname_
}

void cleanup_ignore(ignores *ig) {
size_t i;

if (ig) {
if (ig->regexes) {
for (i = 0; i < ig->regexes_len; i++) {
free(ig->regexes[i]);
}
free(ig->regexes);
}
if (ig->names) {
for (i = 0; i < ig->names_len; i++) {
free(ig->names[i]);
}
free(ig->names);
}
if (ig->abs_path) {
free(ig->abs_path);
}
free(ig);
if (ig == NULL) {
return;
}
free_strings(ig->extensions, ig->extensions_len);
free_strings(ig->names, ig->names_len);
free_strings(ig->slash_names, ig->slash_names_len);
free_strings(ig->regexes, ig->regexes_len);
free_strings(ig->slash_regexes, ig->slash_regexes_len);
if (ig->abs_path) {
free(ig->abs_path);
}
free(ig);
}

void add_ignore_pattern(ignores *ig, const char *pattern) {
@@ -121,7 +115,11 @@ void add_ignore_pattern(ignores *ig, const char *pattern) {
char ***patterns_p;
size_t *patterns_len;
if (is_fnmatch(pattern)) {
if (pattern[0] == '/') {
if (pattern[0] == '*' && pattern[1] == '.' && !(is_fnmatch(pattern + 2))) {
patterns_p = &(ig->extensions);
patterns_len = &(ig->extensions_len);
pattern += 2;
} else if (pattern[0] == '/') {
patterns_p = &(ig->slash_regexes);
patterns_len = &(ig->slash_regexes_len);
pattern++;
@@ -157,7 +155,7 @@ void add_ignore_pattern(ignores *ig, const char *pattern) {
}
patterns[i] = ag_strndup(pattern, pattern_len);
log_debug("added ignore pattern %s to %s", pattern,
ig == root_ignores ? "root ignores" : ig->abs_path);
ig == root_ignores ? "root ignores" : ig->abs_path);
}

/* For loading git/hg ignore patterns */
@@ -266,24 +264,19 @@ static int ackmate_dir_match(const char *dir_name) {
return pcre_exec(opts.ackmate_dir_filter, NULL, dir_name, strlen(dir_name), 0, 0, NULL, 0);
}

/* This is the hottest code in Ag. 10-15% of all execution time is spent here */
static int path_ignore_search(const ignores *ig, const char *path, const char *filename) {
char *temp;

size_t i;
int match_pos;

if (strncmp(filename, "./", 2) == 0) {
filename++;
}

match_pos = binary_search(filename, ig->names, 0, ig->names_len);
if (match_pos >= 0) {
log_debug("file %s ignored because name matches static pattern %s", filename, ig->names[match_pos]);
return 1;
}

ag_asprintf(&temp, "%s/%s", path[0] == '.' ? path + 1 : path, filename);
log_debug("temp: %s abs path: %s", temp, ig->abs_path);

if (strncmp(temp, ig->abs_path, ig->abs_path_len) == 0) {
char *slash_filename = temp + ig->abs_path_len;
@@ -336,8 +329,6 @@ static int path_ignore_search(const ignores *ig, const char *path, const char *f
log_debug("pattern %s doesn't match file %s", ig->regexes[i], filename);
}

log_debug("file %s not ignored", filename);

int rv = ackmate_dir_match(temp);
free(temp);
return rv;
@@ -356,13 +347,7 @@ int filename_filter(const char *path, const struct dirent *dir, void *baton) {
const char *path_start = path;
char *temp;

if (!opts.follow_symlinks && is_symlink(path, dir)) {
log_debug("File %s ignored becaused it's a symlink", dir->d_name);
return 0;
}

if (is_named_pipe(path, dir)) {
log_debug("%s ignored because it's a named pipe", path);
if (!opts.search_hidden_files && filename[0] == '.') {
return 0;
}

@@ -372,9 +357,16 @@ int filename_filter(const char *path, const struct dirent *dir, void *baton) {
}
}

if (!opts.search_hidden_files && filename[0] == '.') {
if (!opts.follow_symlinks && is_symlink(path, dir)) {
log_debug("File %s ignored becaused it's a symlink", dir->d_name);
return 0;
}

if (is_named_pipe(path, dir)) {
log_debug("%s ignored because it's a named pipe", path);
return 0;
}

if (opts.search_all_files && !opts.path_to_agignore) {
return 1;
}
@@ -385,7 +377,31 @@ int filename_filter(const char *path, const struct dirent *dir, void *baton) {
}
log_debug("path_start %s filename %s", path_start, filename);

const char *extension = NULL;
for (i = filename_len - 1; filename_len > 1; i--) {
if (filename[i] == '.') {
extension = filename + i + 1;
break;
}
}
if (extension && extension[0] == '\0') {
extension = NULL;
}

while (ig != NULL) {
if (strncmp(filename, "./", 2) == 0) {
filename++;
filename_len--;
}

if (extension) {
int match_pos = binary_search(extension, ig->extensions, 0, ig->extensions_len);
if (match_pos >= 0) {
log_debug("file %s ignored because name matches extension %s", extension, ig->extensions[match_pos]);
return 0;
}
}

if (path_ignore_search(ig, path_start, filename)) {
return 0;
}
@@ -401,5 +417,6 @@ int filename_filter(const char *path, const struct dirent *dir, void *baton) {
ig = ig->parent;
}

log_debug("%s not ignored", filename);
return 1;
}
Copy path View file
@@ -9,6 +9,9 @@
#define SVN_PROP_IGNORE "svn:ignore"

struct ignores {
char **extensions; /* File extensions to ignore */
size_t extensions_len;

char **names; /* Non-regex ignore lines. Sorted so we can binary search them. */
size_t names_len;
char **slash_names; /* Same but starts with a slash */
@@ -43,6 +46,6 @@ void load_svn_ignore_patterns(ignores *ig, const char *path);

int filename_filter(const char *path, const struct dirent *dir, void *baton);

int is_empty(ignores* ig);
int is_empty(ignores *ig);

#endif
Copy path View file
@@ -157,7 +157,7 @@ int main(int argc, char **argv) {
log_debug("searching path %s for %s", paths[i], opts.query);
symhash = NULL;
ignores *ig = init_ignore(root_ignores, "", 0);
struct stat s = {.st_dev = 0};
struct stat s = { .st_dev = 0 };
#ifndef _WIN32
/* The device is ignored if opts.one_dev is false, so it's fine
* to leave it at the default 0
Copy path View file
@@ -205,7 +205,7 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
{ "color-path", required_argument, NULL, 0 },
{ "column", no_argument, &opts.column, 1 },
{ "context", optional_argument, NULL, 'C' },
{ "count", no_argument, NULL, 'c'},
{ "count", no_argument, NULL, 'c' },
{ "debug", no_argument, NULL, 'D' },
{ "depth", required_argument, NULL, 0 },
{ "filename", no_argument, NULL, 0 },
@@ -250,7 +250,7 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
{ "passthrough", no_argument, &opts.passthrough, 1 },
{ "passthru", no_argument, &opts.passthrough, 1 },
{ "path-to-agignore", required_argument, NULL, 'p' },
{ "print0", no_argument, NULL, '0'},
{ "print0", no_argument, NULL, '0' },
{ "print-long-lines", no_argument, &opts.print_long_lines, 1 },
{ "recurse", no_argument, NULL, 'r' },
{ "search-binary", no_argument, &opts.search_binary_files, 1 },
Copy path View file
@@ -52,6 +52,17 @@ char *ag_strndup(const char *s, size_t size) {
#endif
}

void free_strings(char **strs, const size_t strs_len) {
if (strs == NULL) {
return;
}
size_t i;
for (i = 0; i < strs_len; i++) {
free(strs[i]);
}
free(strs);
}

void generate_alpha_skip(const char *find, size_t f_len, size_t skip_lookup[], const int case_sensitive) {
size_t i;

Copy path View file
@@ -52,6 +52,8 @@ ag_stats stats;

typedef const char *(*strncmp_fp)(const char *, const char *, const size_t, const size_t, const size_t[], const size_t *);

void free_strings(char **strs, const size_t strs_len);

void generate_alpha_skip(const char *find, size_t f_len, size_t skip_lookup[], const int case_sensitive);
int is_prefix(const char *s, const size_t s_len, const size_t pos, const int case_sensitive);
size_t suffix_len(const char *s, const size_t s_len, const size_t pos, const int case_sensitive);
ProTip! Use n and p to navigate between commits in a pull request.