git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 2/3] exclude: do strcmp as much as possible before fnmatch

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
May 26, 2012, 12:31 UTC
Message-ID
<1338035474-4346-3-git-send-email-pclouds@gmail.com>
In-Reply-To
<1338035474-4346-1-git-send-email-pclouds@gmail.com>

this also avoids calling fnmatch() if the non-wildcard prefix is longer than basename

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 dir.c | 41 +++++++++++++++++++++++++++--------------
 dir.h |  2 +-
 2 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/dir.c b/dir.c
index 8535cf2..50d744f 100644
--- a/dir.c
+++ b/dir.c
@@ -295,9 +295,11 @@ int match_pathspec_depth(const struct pathspec *ps,
 	return retval;
 }
 
+const char *wildcards = "*?[{\\";
+
 static int no_wildcard(const char *string)
 {
-	return string[strcspn(string, "*?[{\\")] == '\0';
+	return string[strcspn(string, wildcards)] == '\0';
 }
 
 void add_exclude(const char *string, const char *base,
@@ -336,8 +338,7 @@ void add_exclude(const char *string, const char *base,
 	x->flags = flags;
 	if (!strchr(string, '/'))
 		x->flags |= EXC_FLAG_NODIR;
-	if (no_wildcard(string))
-		x->flags |= EXC_FLAG_NOWILDCARD;
+	x->nowildcardlen = strcspn(string, wildcards);
 	if (*string == '*' && no_wildcard(string+1))
 		x->flags |= EXC_FLAG_ENDSWITH;
 	ALLOC_GROW(which->excludes, which->nr + 1, which->alloc);
@@ -513,8 +514,9 @@ int excluded_from_list(const char *pathname,
 
 	for (i = el->nr - 1; 0 <= i; i--) {
 		struct exclude *x = el->excludes[i];
-		const char *exclude = x->pattern;
+		const char *name, *exclude = x->pattern;
 		int to_exclude = x->to_exclude;
+		int namelen, prefix = x->nowildcardlen;
 
 		if (x->flags & EXC_FLAG_MUSTBEDIR) {
 			if (*dtype == DT_UNKNOWN)
@@ -525,7 +527,7 @@ int excluded_from_list(const char *pathname,
 
 		if (x->flags & EXC_FLAG_NODIR) {
 			/* match basename */
-			if (x->flags & EXC_FLAG_NOWILDCARD) {
+			if (prefix == x->patternlen) {
 				if (!strcmp_icase(exclude, basename))
 					return to_exclude;
 			} else if (x->flags & EXC_FLAG_ENDSWITH) {
@@ -539,26 +541,37 @@ int excluded_from_list(const char *pathname,
 			continue;
 		}
 
-
 		/* match with FNM_PATHNAME:
 		 * exclude has base (baselen long) implicitly in front of it.
 		 */
-		if (*exclude == '/')
+		if (*exclude == '/') {
 			exclude++;
+			prefix--;
+		}
 
 		if (pathlen < x->baselen ||
 		    (x->baselen && pathname[x->baselen-1] != '/') ||
 		    strncmp_icase(pathname, x->base, x->baselen))
 			continue;
 
-		if (x->flags & EXC_FLAG_NOWILDCARD) {
-			if (!strcmp_icase(exclude, pathname + x->baselen))
-				return to_exclude;
-		} else {
-			if (fnmatch_icase(exclude, pathname+x->baselen,
-					  FNM_PATHNAME) == 0)
-				return to_exclude;
+		namelen = x->baselen ? pathlen - x->baselen : pathlen;
+		name = pathname + pathlen  - namelen;
+
+		/* if the non-wildcard part is longer than the
+		   remaining pathname, surely it cannot match */
+		if (prefix > namelen)
+			continue;
+
+		if (prefix) {
+			if (strncmp_icase(exclude, name, prefix))
+				continue;
+			exclude += prefix;
+			name    += prefix;
+			namelen -= prefix;
 		}
+
+		if (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))
+			return to_exclude;
 	}
 	return -1; /* undecided */
 }
diff --git a/dir.h b/dir.h
index 58b6fc7..39fc145 100644
--- a/dir.h
+++ b/dir.h
@@ -7,7 +7,6 @@ struct dir_entry {
 };
 
 #define EXC_FLAG_NODIR 1
-#define EXC_FLAG_NOWILDCARD 2
 #define EXC_FLAG_ENDSWITH 4
 #define EXC_FLAG_MUSTBEDIR 8
 
@@ -17,6 +16,7 @@ struct exclude_list {
 	struct exclude {
 		const char *pattern;
 		int patternlen;
+		int nowildcardlen;
 		const char *base;
 		int baselen;
 		int to_exclude;
-- 
1.7.10.2.549.g9354186
Previous: Nguyễn Thái Ngọc DuyNext: Junio C Hamano
Message 3 of 14 in “top-level gitignore considered less harmful”
  1. 0/3 top-level gitignore considered less harmfulNguyễn Thái Ngọc Duy, May 26, 2012
  2. 1/3 Unindent excluded_from_list()Nguyễn Thái Ngọc Duy, May 26, 2012
  3. 2/3 exclude: do strcmp as much as possible before fnmatchNguyễn Thái Ngọc Duy, May 26, 2012
  4. Junio C HamanoMay 27, 2012
  5. Nguyen Thai Ngoc DuyMay 27, 2012
  6. Junio C HamanoMay 27, 2012
  7. Nguyen Thai Ngoc DuyMay 28, 2012
  8. Junio C HamanoMay 28, 2012
  9. Junio C HamanoMay 29, 2012
  10. Thiago FarinaMay 29, 2012
  11. 3/3 exclude: reduce computation cost on checking dirname in patternsNguyễn Thái Ngọc Duy, May 26, 2012
  12. Nguyen Thai Ngoc DuyMay 26, 2012
  13. Jeff KingMay 26, 2012
  14. Nguyen Thai Ngoc DuyMay 27, 2012

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.