summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@microsoft.com>2015-04-29 17:23:02 -0400
committerEdward Thomson <ethomson@microsoft.com>2015-05-01 12:31:40 -0400
commitf63a1b729bc74e1e72f80f75843b8f2042f3f81f (patch)
tree51e1fe8975ef4f5c798d42f69a82df9c4df1cf82
parent5c387b6c5a616d245e51e4ca1935e6ffd78c710e (diff)
downloadlibgit2-f63a1b729bc74e1e72f80f75843b8f2042f3f81f.tar.gz
git_path_diriter: use FindFirstFile in win32
Using FindFirstFile and FindNextFile in win32 allows us to use the directory information that is returned, instead of us having to get the file attributes all over again, which is a distinct cost savings on win32.
-rw-r--r--src/iterator.c2
-rw-r--r--src/path.c188
-rw-r--r--src/path.h26
-rw-r--r--src/win32/path_w32.c5
4 files changed, 213 insertions, 8 deletions
diff --git a/src/iterator.c b/src/iterator.c
index 52814bae7..c5c5fd7ce 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -1011,7 +1011,7 @@ static int dirload_with_stat(
const char *end_stat,
git_vector *contents)
{
- git_path_diriter diriter = {0};
+ git_path_diriter diriter = GIT_PATH_DIRITER_INIT;
const char *path;
int (*strncomp)(const char *a, const char *b, size_t sz);
size_t start_len = start_stat ? strlen(start_stat) : 0;
diff --git a/src/path.c b/src/path.c
index ee566985a..6c9852b79 100644
--- a/src/path.c
+++ b/src/path.c
@@ -1078,6 +1078,182 @@ int git_path_direach(
return error;
}
+#if defined(GIT_WIN32) && !defined(__MINGW32__)
+
+/* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7
+ * and better. Prior versions will ignore this.
+ */
+#ifndef FIND_FIRST_EX_LARGE_FETCH
+# define FIND_FIRST_EX_LARGE_FETCH 2
+#endif
+
+int git_path_diriter_init(
+ git_path_diriter *diriter,
+ const char *path,
+ unsigned int flags)
+{
+ git_win32_path path_filter;
+ git_buf hack = {0};
+
+ assert(diriter && path);
+
+ memset(diriter, 0, sizeof(git_path_diriter));
+ diriter->handle = INVALID_HANDLE_VALUE;
+
+ if (git_buf_puts(&diriter->path_utf8, path) < 0)
+ return -1;
+
+ git_path_trim_slashes(&diriter->path_utf8);
+
+ if (diriter->path_utf8.size == 0) {
+ giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path);
+ return -1;
+ }
+
+ if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 ||
+ !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) {
+ giterr_set(GITERR_OS, "Could not parse the directory path '%s'", path);
+ return -1;
+ }
+
+ diriter->handle = FindFirstFileExW(
+ path_filter,
+ FindExInfoBasic,
+ &diriter->current,
+ FindExSearchNameMatch,
+ NULL,
+ FIND_FIRST_EX_LARGE_FETCH);
+
+ if (diriter->handle == INVALID_HANDLE_VALUE) {
+ giterr_set(GITERR_OS, "Could not open directory '%s'", path);
+ return -1;
+ }
+
+ diriter->parent_utf8_len = diriter->path_utf8.size;
+ diriter->flags = flags;
+ return 0;
+}
+
+static int diriter_update_utf16(git_path_diriter *diriter)
+{
+ size_t filename_len, path_len;
+
+ filename_len = wcslen(diriter->current.cFileName);
+
+ if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) ||
+ GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2))
+ return -1;
+
+ if (path_len > GIT_WIN_PATH_UTF16) {
+ giterr_set(GITERR_FILESYSTEM,
+ "invalid path '%.*ls\\%ls' (path too long)",
+ diriter->parent_len, diriter->path, diriter->current.cFileName);
+ return -1;
+ }
+
+ diriter->path[diriter->parent_len] = L'\\';
+ memcpy(&diriter->path[diriter->parent_len+1],
+ diriter->current.cFileName, filename_len * sizeof(wchar_t));
+ diriter->path[path_len-1] = L'\0';
+
+ return 0;
+}
+
+static int diriter_update_utf8(git_path_diriter *diriter)
+{
+ git_win32_utf8_path filename_utf8;
+ wchar_t *filename_utf16;
+ int filename_utf8_len;
+
+ /* Don't copy the full UTF-16 path into the UTF-8 path, only do the
+ * UTF16 -> UTF8 conversion of the filename portion. This prevents us
+ * from trying to encode the parent path differently, which would be
+ * bad since we do arithmetic based on the already computed parent len.
+ */
+
+ filename_utf16 = &diriter->path[diriter->parent_len + 1];
+
+ if ((filename_utf8_len = git_win32_path_to_utf8(filename_utf8, filename_utf16)) < 0)
+ return filename_utf8_len;
+
+ git_buf_truncate(&diriter->path_utf8, diriter->parent_utf8_len);
+ git_buf_putc(&diriter->path_utf8, '/');
+ git_buf_put(&diriter->path_utf8, filename_utf8, (size_t)filename_utf8_len);
+
+ if (git_buf_oom(&diriter->path_utf8))
+ return -1;
+
+ return 0;
+}
+
+int git_path_diriter_next(git_path_diriter *diriter)
+{
+ bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
+
+ do {
+ /* Our first time through, we already have the data from
+ * FindFirstFileW. Use it, otherwise get the next file.
+ */
+ if (!diriter->needs_next)
+ diriter->needs_next = 1;
+ else if (!FindNextFileW(diriter->handle, &diriter->current))
+ return GIT_ITEROVER;
+ } while (skip_dot && git_path_is_dot_or_dotdotW(diriter->current.cFileName));
+
+ if (diriter_update_utf16(diriter) < 0 || diriter_update_utf8(diriter) < 0)
+ return -1;
+
+ return 0;
+}
+
+int git_path_diriter_filename(
+ const char **out,
+ size_t *out_len,
+ git_path_diriter *diriter)
+{
+ assert(out && out_len && diriter);
+
+ assert(diriter->path_utf8.size > diriter->parent_utf8_len);
+
+ *out = &diriter->path_utf8.ptr[diriter->parent_utf8_len+1];
+ *out_len = diriter->path_utf8.size - diriter->parent_utf8_len - 1;
+ return 0;
+}
+
+int git_path_diriter_fullpath(
+ const char **out,
+ size_t *out_len,
+ git_path_diriter *diriter)
+{
+ assert(out && out_len && diriter);
+
+ *out = diriter->path_utf8.ptr;
+ *out_len = diriter->path_utf8.size;
+ return 0;
+}
+
+int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter)
+{
+ assert(out && diriter);
+
+ return git_win32__file_attribute_to_stat(out,
+ (WIN32_FILE_ATTRIBUTE_DATA *)&diriter->current,
+ diriter->path);
+}
+
+void git_path_diriter_free(git_path_diriter *diriter)
+{
+ if (diriter == NULL)
+ return;
+
+ if (diriter->handle != INVALID_HANDLE_VALUE) {
+ FindClose(diriter->handle);
+ diriter->handle = INVALID_HANDLE_VALUE;
+ }
+}
+
+#else
+
int git_path_diriter_init(
git_path_diriter *diriter,
const char *path,
@@ -1090,9 +1266,13 @@ int git_path_diriter_init(
if (git_buf_puts(&diriter->path, path) < 0)
return -1;
- git_path_mkposix(diriter->path.ptr);
git_path_trim_slashes(&diriter->path);
+ if (diriter->path.size == 0) {
+ giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path);
+ return -1;
+ }
+
if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) {
git_buf_free(&diriter->path);
@@ -1159,6 +1339,8 @@ int git_path_diriter_filename(
{
assert(out && out_len && diriter);
+ assert(diriter->path.size > diriter->parent_len);
+
*out = &diriter->path.ptr[diriter->parent_len+1];
*out_len = diriter->path.size - diriter->parent_len - 1;
return 0;
@@ -1200,13 +1382,15 @@ void git_path_diriter_free(git_path_diriter *diriter)
git_buf_free(&diriter->path);
}
+#endif
+
int git_path_dirload(
git_vector *contents,
const char *path,
size_t prefix_len,
unsigned int flags)
{
- git_path_diriter iter = {0};
+ git_path_diriter iter = GIT_PATH_DIRITER_INIT;
const char *name;
size_t name_len;
char *dup;
diff --git a/src/path.h b/src/path.h
index 927d2fc6e..9c2b85a87 100644
--- a/src/path.h
+++ b/src/path.h
@@ -329,6 +329,28 @@ extern int git_path_walk_up(
typedef struct git_path_diriter git_path_diriter;
+#if defined(GIT_WIN32) && !defined(__MINGW32__)
+
+struct git_path_diriter
+{
+ git_win32_path path;
+ size_t parent_len;
+
+ git_buf path_utf8;
+ size_t parent_utf8_len;
+
+ HANDLE handle;
+
+ unsigned int flags;
+
+ WIN32_FIND_DATAW current;
+ unsigned int needs_next;
+};
+
+#define GIT_PATH_DIRITER_INIT { {0}, 0, GIT_BUF_INIT, 0, INVALID_HANDLE_VALUE }
+
+#else
+
struct git_path_diriter
{
git_buf path;
@@ -339,6 +361,10 @@ struct git_path_diriter
DIR *dir;
};
+#define GIT_PATH_DIRITER_INIT { GIT_BUF_INIT }
+
+#endif
+
/**
* Initialize a directory iterator.
*
diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c
index c145379f7..118e8bcc5 100644
--- a/src/win32/path_w32.c
+++ b/src/win32/path_w32.c
@@ -30,11 +30,6 @@
#define path__is_unc(p) \
(((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/'))
-/* 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;