diff options
author | Marius Storm-Olsen <marius@trolltech.com> | 2009-05-31 18:15:22 +0200 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2009-06-01 00:25:54 -0700 |
commit | e16c60d9f98c66fd2c9bba64562b3439970c13f8 (patch) | |
tree | ac682110cc4fc94da135be7e5f3033119e3e496e /compat | |
parent | 36ad53ffee6ed5b7c277cde660f526fd8ce3d68f (diff) | |
download | git-e16c60d9f98c66fd2c9bba64562b3439970c13f8.tar.gz |
MinGW readdir reimplementation to support d_type
The original readdir implementation was fast, but didn't
support the d_type. This means that git would do additional
lstats for each entry, to figure out if the entry was a
directory or not. This unneedingly slowed down many
operations, since Windows API provides this information
directly when walking the directories.
By running this implementation on Moe's repo structure:
mkdir bummer && cd bummer; for ((i=0;i<100;i++)); do
mkdir $i && pushd $i;
for ((j=0;j<1000;j++)); do echo "$j" >$j; done;
popd;
done
We see the following speedups:
git add .
-------------------
old: 00:00:23(.087)
new: 00:00:21(.512) 1.07x
git status
-------------------
old: 00:00:03(.306)
new: 00:00:01(.684) 1.96x
git clean -dxf
-------------------
old: 00:00:01(.918)
new: 00:00:00(.295) 6.50x
Signed-off-by: Marius Storm-Olsen <marius@trolltech.com>
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'compat')
-rw-r--r-- | compat/mingw.c | 59 | ||||
-rw-r--r-- | compat/mingw.h | 29 |
2 files changed, 88 insertions, 0 deletions
diff --git a/compat/mingw.c b/compat/mingw.c index 12d0c2fd8d..bed417875e 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1172,3 +1172,62 @@ char *getpass(const char *prompt) fputs("\n", stderr); return strbuf_detach(&buf, NULL); } + +#ifndef NO_MINGW_REPLACE_READDIR +/* MinGW readdir implementation to avoid extra lstats for Git */ +struct mingw_DIR +{ + struct _finddata_t dd_dta; /* disk transfer area for this dir */ + struct mingw_dirent dd_dir; /* Our own implementation, including d_type */ + long dd_handle; /* _findnext handle */ + int dd_stat; /* 0 = next entry to read is first entry, -1 = off the end, positive = 0 based index of next entry */ + char dd_name[1]; /* given path for dir with search pattern (struct is extended) */ +}; + +struct dirent *mingw_readdir(DIR *dir) +{ + WIN32_FIND_DATAA buf; + HANDLE handle; + struct mingw_DIR *mdir = (struct mingw_DIR*)dir; + + if (!dir->dd_handle) { + errno = EBADF; /* No set_errno for mingw */ + return NULL; + } + + if (dir->dd_handle == (long)INVALID_HANDLE_VALUE && dir->dd_stat == 0) + { + handle = FindFirstFileA(dir->dd_name, &buf); + DWORD lasterr = GetLastError(); + dir->dd_handle = (long)handle; + if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) { + errno = err_win_to_posix(lasterr); + return NULL; + } + } else if (dir->dd_handle == (long)INVALID_HANDLE_VALUE) { + return NULL; + } else if (!FindNextFileA((HANDLE)dir->dd_handle, &buf)) { + DWORD lasterr = GetLastError(); + FindClose((HANDLE)dir->dd_handle); + dir->dd_handle = (long)INVALID_HANDLE_VALUE; + /* POSIX says you shouldn't set errno when readdir can't + find any more files; so, if another error we leave it set. */ + if (lasterr != ERROR_NO_MORE_FILES) + errno = err_win_to_posix(lasterr); + return NULL; + } + + /* We get here if `buf' contains valid data. */ + strcpy(dir->dd_dir.d_name, buf.cFileName); + ++dir->dd_stat; + + /* Set file type, based on WIN32_FIND_DATA */ + mdir->dd_dir.d_type = 0; + if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + mdir->dd_dir.d_type |= DT_DIR; + else + mdir->dd_dir.d_type |= DT_REG; + + return (struct dirent*)&dir->dd_dir; +} +#endif // !NO_MINGW_REPLACE_READDIR diff --git a/compat/mingw.h b/compat/mingw.h index 4c50f5b1bc..4f7ba4c13f 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -235,3 +235,32 @@ int main(int argc, const char **argv) \ return mingw_main(argc, argv); \ } \ static int mingw_main(c,v) + +#ifndef NO_MINGW_REPLACE_READDIR +/* + * A replacement of readdir, to ensure that it reads the file type at + * the same time. This avoid extra unneeded lstats in git on MinGW + */ +#undef DT_UNKNOWN +#undef DT_DIR +#undef DT_REG +#undef DT_LNK +#define DT_UNKNOWN 0 +#define DT_DIR 1 +#define DT_REG 2 +#define DT_LNK 3 + +struct mingw_dirent +{ + long d_ino; /* Always zero. */ + union { + unsigned short d_reclen; /* Always zero. */ + unsigned char d_type; /* Reimplementation adds this */ + }; + unsigned short d_namlen; /* Length of name in d_name. */ + char d_name[FILENAME_MAX]; /* File name. */ +}; +#define dirent mingw_dirent +#define readdir(x) mingw_readdir(x) +struct dirent *mingw_readdir(DIR *dir); +#endif // !NO_MINGW_REPLACE_READDIR |