summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@microsoft.com>2015-04-28 11:16:42 -0400
committerEdward Thomson <ethomson@microsoft.com>2015-04-28 14:25:13 -0400
commitb3f6cef066844fb27e649b8f2c0daca6f596bc81 (patch)
tree5793444521e9b263e544c62051c6b1e119108780
parente05531ddbdb03fb7698f8122a71862ad11afe812 (diff)
downloadlibgit2-b3f6cef066844fb27e649b8f2c0daca6f596bc81.tar.gz
dirload: loop conditional; less path mangling
-rw-r--r--src/win32/path_w32.c161
1 files changed, 102 insertions, 59 deletions
diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c
index f793cbd1f..eda6e2696 100644
--- a/src/win32/path_w32.c
+++ b/src/win32/path_w32.c
@@ -32,6 +32,11 @@
#define PATH__MAX_UNC_LEN (32767)
+/* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7
+ * and better. Prior versions will ignore this.
+ */
+#define _FIND_FIRST_EX_LARGE_FETCH 2
+
GIT_INLINE(int) path__cwd(wchar_t *path, int size)
{
int len;
@@ -309,6 +314,45 @@ char *git_win32_path_8dot3_name(const char *path)
return shortname;
}
+GIT_INLINE(int) path_with_stat_alloc(
+ git_path_with_stat **out,
+ const char *parent_path,
+ size_t parent_path_len,
+ const char *child_path,
+ size_t child_path_len,
+ bool trailing_slash)
+{
+ git_path_with_stat *ps;
+ int inner_slash =
+ (parent_path_len > 0 && parent_path[parent_path_len-1] != '/');
+ size_t path_len, ps_size;
+
+ GITERR_CHECK_ALLOC_ADD(&path_len, parent_path_len, inner_slash);
+ GITERR_CHECK_ALLOC_ADD(&path_len, path_len, child_path_len);
+ GITERR_CHECK_ALLOC_ADD(&path_len, path_len, trailing_slash ? 1 : 0);
+
+ GITERR_CHECK_ALLOC_ADD(&ps_size, sizeof(git_path_with_stat), path_len);
+
+ ps = git__calloc(1, ps_size);
+ GITERR_CHECK_ALLOC(ps);
+
+ if (parent_path_len)
+ memcpy(ps->path, parent_path, parent_path_len);
+
+ if (inner_slash)
+ ps->path[parent_path_len] = '/';
+
+ memcpy(&ps->path[parent_path_len + inner_slash], child_path, child_path_len);
+
+ if (trailing_slash)
+ ps->path[path_len-1] = '/';
+
+ ps->path_len = path_len;
+
+ *out = ps;
+ return 0;
+}
+
#if !defined(__MINGW32__)
int git_win32_path_dirload_with_stat(
const char *path,
@@ -326,11 +370,9 @@ int git_win32_path_dirload_with_stat(
size_t cmp_len;
size_t start_len = start_stat ? strlen(start_stat) : 0;
size_t end_len = end_stat ? strlen(end_stat) : 0;
- size_t path_size = strlen(path);
- const char *repo_path = path + prefix_len;
- size_t repo_path_len = strlen(repo_path);
char work_path[PATH__MAX_UNC_LEN];
- size_t path_len;
+ const char *suffix;
+ size_t path_len, work_path_len, suffix_len;
if (!git_win32__findfirstfile_filter(pathw, path)) {
giterr_set(GITERR_OS, "Could not parse the path '%s'", path);
@@ -338,14 +380,23 @@ int git_win32_path_dirload_with_stat(
}
strncomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ?
- git__strncasecmp : git__strncmp;
+ git__strncasecmp : git__strncmp;
+
+ path_len = strlen(path);
+ suffix = path + prefix_len;
+ suffix_len = path_len - prefix_len;
/* use of FIND_FIRST_EX_LARGE_FETCH flag in the FindFirstFileExW call could benefit perormance
* here when querying large repositories on Windows 7 (0x0600) or newer versions of Windows.
* doing so could introduce compatibility issues on older versions of Windows. */
- dir.h = FindFirstFileExW(pathw, FindExInfoBasic, &dir.f, FindExSearchNameMatch, NULL, 0);
- dir.first = 1;
+ dir.h = FindFirstFileExW(
+ pathw,
+ FindExInfoBasic,
+ &dir.f,
+ FindExSearchNameMatch,
+ NULL,
+ _FIND_FIRST_EX_LARGE_FETCH);
if (dir.h == INVALID_HANDLE_VALUE) {
error = -1;
@@ -353,64 +404,56 @@ int git_win32_path_dirload_with_stat(
goto clean_up_and_exit;
}
- if (repo_path_len > PATH__MAX_UNC_LEN) {
- error = -1;
- giterr_set(GITERR_OS, "Could not open directory '%s'", path);
- goto clean_up_and_exit;
- }
-
- memcpy(work_path, repo_path, repo_path_len);
-
- while (1) {
- if (!git_path_is_dot_or_dotdotW(dir.f.cFileName)) {
- path_len = git__utf16_to_8(work_path + repo_path_len, ARRAYSIZE(work_path) - repo_path_len, dir.f.cFileName);
-
- work_path[path_len + repo_path_len] = '\0';
- path_len = path_len + repo_path_len;
-
- cmp_len = min(start_len, path_len);
- if (!(cmp_len && strncomp(work_path, start_stat, cmp_len) < 0)) {
- cmp_len = min(end_len, path_len);
-
- if (!(cmp_len && strncomp(work_path, end_stat, cmp_len) > 0)) {
- ps = git__calloc(1, sizeof(git_path_with_stat) + path_len + 2);
-
- if ((error = git_win32__file_attribute_to_stat(&ps->st,
- (WIN32_FILE_ATTRIBUTE_DATA *)&dir.f,
- NULL)) < 0) {
- git__free(ps);
- goto clean_up_and_exit;
- }
+ do {
+ if (git_path_is_dot_or_dotdotW(dir.f.cFileName))
+ continue;
+
+ if ((work_path_len = git__utf16_to_8(work_path, PATH__MAX_UNC_LEN, dir.f.cFileName)) < 0) {
+ error = -1;
+ giterr_set(GITERR_OS, "Could not convert path to UTF-8 (path too long?)");
+ goto clean_up_and_exit;
+ }
+ work_path[work_path_len] = '\0';
+
+ /* TODO: what about junctions to directories? */
+ if ((error = path_with_stat_alloc(&ps,
+ suffix, suffix_len,
+ work_path, work_path_len,
+ (dir.f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)) < 0)
+ goto clean_up_and_exit;
+
+ /* skip if before start_stat or after end_stat */
+ cmp_len = min(start_len, work_path_len);
+ if (cmp_len && strncomp(ps->path, start_stat, cmp_len) < 0) {
+ git__free(ps);
+ continue;
+ }
- memcpy(ps->path, work_path, path_len + 1);
- ps->path_len = path_len;
+ cmp_len = min(end_len, work_path_len);
+ if (cmp_len && strncomp(ps->path, end_stat, cmp_len) > 0) {
+ git__free(ps);
+ continue;
+ }
- if (S_ISDIR(ps->st.st_mode)) {
- ps->path[ps->path_len++] = '/';
- ps->path[ps->path_len] = '\0';
- } else if (!S_ISREG(ps->st.st_mode) && !S_ISLNK(ps->st.st_mode)) {
- git__free(ps);
- ps = NULL;
- }
+ if ((error = git_win32__file_attribute_to_stat(&ps->st,
+ (WIN32_FILE_ATTRIBUTE_DATA *)&dir.f,
+ NULL)) < 0) {
+ git__free(ps);
+ goto clean_up_and_exit;
+ }
- if (ps)
- git_vector_insert(contents, ps);
- }
- }
+ if (!S_ISDIR(ps->st.st_mode) && !S_ISREG(ps->st.st_mode) && !S_ISLNK(ps->st.st_mode)) {
+ git__free(ps);
+ continue;
}
- memset(&dir.f, 0, sizeof(git_path_with_stat));
- dir.first = 0;
+ git_vector_insert(contents, ps);
+ } while (FindNextFileW(dir.h, &dir.f));
- if (!FindNextFileW(dir.h, &dir.f)) {
- if (GetLastError() == ERROR_NO_MORE_FILES)
- break;
- else {
- error = -1;
- giterr_set(GITERR_OS, "Could not get attributes for file in '%s'", path);
- goto clean_up_and_exit;
- }
- }
+ if (GetLastError() != ERROR_NO_MORE_FILES) {
+ error = -1;
+ giterr_set(GITERR_OS, "Could not get attributes for file in '%s'", path);
+ goto clean_up_and_exit;
}
/* sort now that directory suffix is added */