diff options
author | Edward Thomson <ethomson@microsoft.com> | 2015-04-28 11:16:42 -0400 |
---|---|---|
committer | Edward Thomson <ethomson@microsoft.com> | 2015-04-28 14:25:13 -0400 |
commit | b3f6cef066844fb27e649b8f2c0daca6f596bc81 (patch) | |
tree | 5793444521e9b263e544c62051c6b1e119108780 | |
parent | e05531ddbdb03fb7698f8122a71862ad11afe812 (diff) | |
download | libgit2-b3f6cef066844fb27e649b8f2c0daca6f596bc81.tar.gz |
dirload: loop conditional; less path mangling
-rw-r--r-- | src/win32/path_w32.c | 161 |
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 */ |