blob: c6777148d203e1de2768e2a76a4633e0823452c8 [file] [log] [blame]
From 5c2b396f0c1b08fb152981fd1105bd219a82570d Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Fri, 6 Sep 2019 21:09:35 +0200
Subject: is_ntfs_dotgit(): speed it up
commit 3a85dc7d534fc2d410ddc0c771c963b20d1b4857 upstream.
Previously, this function was written without focusing on speed,
intending to make reviewing the code as easy as possible, to avoid any
bugs in this critical code.
Turns out: we can do much better on both accounts. With this patch, we
make it as fast as this developer can make it go:
- We avoid the call to `is_dir_sep()` and make all the character
comparisons explicit.
- We avoid the cost of calling `strncasecmp()` and unroll the test for
`.git` and `git~1`, not even using `tolower()` because it is faster to
compare against two constant values.
- We look for `.git` and `.git~1` first thing, and return early if not
found.
- We also avoid calling a separate function for detecting chains of
spaces and periods.
Each of these improvements has a noticeable impact on the speed of
`is_ntfs_dotgit()`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
path.c | 55 ++++++++++++++++++++++++++++++-------------------------
1 file changed, 30 insertions(+), 25 deletions(-)
diff --git a/path.c b/path.c
index 93e321bbd0..83006824b3 100644
--- a/path.c
+++ b/path.c
@@ -1219,20 +1219,6 @@ int daemon_avoid_alias(const char *p)
}
}
-static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
-{
- if (len < skip)
- return 0;
- len -= skip;
- path += skip;
- while (len-- > 0) {
- char c = *(path++);
- if (c != ' ' && c != '.')
- return 0;
- }
- return 1;
-}
-
/*
* On NTFS, we need to be careful to disallow certain synonyms of the `.git/`
* directory:
@@ -1272,19 +1258,38 @@ static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
*/
int is_ntfs_dotgit(const char *name)
{
- size_t len;
+ char c;
- for (len = 0; ; len++)
- if (!name[len] || name[len] == '\\' || is_dir_sep(name[len]) ||
- name[len] == ':') {
- if (only_spaces_and_periods(name, len, 4) &&
- !strncasecmp(name, ".git", 4))
- return 1;
- if (only_spaces_and_periods(name, len, 5) &&
- !strncasecmp(name, "git~1", 5))
- return 1;
+ /*
+ * Note that when we don't find `.git` or `git~1` we end up with `name`
+ * advanced partway through the string. That's okay, though, as we
+ * return immediately in those cases, without looking at `name` any
+ * further.
+ */
+ c = *(name++);
+ if (c == '.') {
+ /* .git */
+ if (((c = *(name++)) != 'g' && c != 'G') ||
+ ((c = *(name++)) != 'i' && c != 'I') ||
+ ((c = *(name++)) != 't' && c != 'T'))
return 0;
- }
+ } else if (c == 'g' || c == 'G') {
+ /* git ~1 */
+ if (((c = *(name++)) != 'i' && c != 'I') ||
+ ((c = *(name++)) != 't' && c != 'T') ||
+ *(name++) != '~' ||
+ *(name++) != '1')
+ return 0;
+ } else
+ return 0;
+
+ for (;;) {
+ c = *(name++);
+ if (!c || c == '\\' || c == '/' || c == ':')
+ return 1;
+ if (c != '.' && c != ' ')
+ return 0;
+ }
}
static int is_ntfs_dot_generic(const char *name,
--
2.24.0.393.g34dc348eaf