]> git.proxmox.com Git - libgit2.git/commitdiff
dirload: loop conditional; less path mangling
authorEdward Thomson <ethomson@microsoft.com>
Tue, 28 Apr 2015 15:16:42 +0000 (11:16 -0400)
committerEdward Thomson <ethomson@microsoft.com>
Tue, 28 Apr 2015 18:25:13 +0000 (14:25 -0400)
src/win32/path_w32.c

index f793cbd1f4c1a34636d7199908b74e06025c2013..eda6e2696d88fd3f32fd1eb69064f9771e09ddcc 100644 (file)
 
 #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 */