Commit f63a1b72 by Edward Thomson

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.
parent 5c387b6c
......@@ -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;
......
......@@ -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;
......
......@@ -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.
*
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment