Skip to content

Commit e16c60d

Browse files
Marius Storm-Olsengitster
authored andcommitted
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 <[email protected]> Signed-off-by: Steffen Prohaska <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 36ad53f commit e16c60d

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

compat/mingw.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,3 +1172,62 @@ char *getpass(const char *prompt)
11721172
fputs("\n", stderr);
11731173
return strbuf_detach(&buf, NULL);
11741174
}
1175+
1176+
#ifndef NO_MINGW_REPLACE_READDIR
1177+
/* MinGW readdir implementation to avoid extra lstats for Git */
1178+
struct mingw_DIR
1179+
{
1180+
struct _finddata_t dd_dta; /* disk transfer area for this dir */
1181+
struct mingw_dirent dd_dir; /* Our own implementation, including d_type */
1182+
long dd_handle; /* _findnext handle */
1183+
int dd_stat; /* 0 = next entry to read is first entry, -1 = off the end, positive = 0 based index of next entry */
1184+
char dd_name[1]; /* given path for dir with search pattern (struct is extended) */
1185+
};
1186+
1187+
struct dirent *mingw_readdir(DIR *dir)
1188+
{
1189+
WIN32_FIND_DATAA buf;
1190+
HANDLE handle;
1191+
struct mingw_DIR *mdir = (struct mingw_DIR*)dir;
1192+
1193+
if (!dir->dd_handle) {
1194+
errno = EBADF; /* No set_errno for mingw */
1195+
return NULL;
1196+
}
1197+
1198+
if (dir->dd_handle == (long)INVALID_HANDLE_VALUE && dir->dd_stat == 0)
1199+
{
1200+
handle = FindFirstFileA(dir->dd_name, &buf);
1201+
DWORD lasterr = GetLastError();
1202+
dir->dd_handle = (long)handle;
1203+
if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
1204+
errno = err_win_to_posix(lasterr);
1205+
return NULL;
1206+
}
1207+
} else if (dir->dd_handle == (long)INVALID_HANDLE_VALUE) {
1208+
return NULL;
1209+
} else if (!FindNextFileA((HANDLE)dir->dd_handle, &buf)) {
1210+
DWORD lasterr = GetLastError();
1211+
FindClose((HANDLE)dir->dd_handle);
1212+
dir->dd_handle = (long)INVALID_HANDLE_VALUE;
1213+
/* POSIX says you shouldn't set errno when readdir can't
1214+
find any more files; so, if another error we leave it set. */
1215+
if (lasterr != ERROR_NO_MORE_FILES)
1216+
errno = err_win_to_posix(lasterr);
1217+
return NULL;
1218+
}
1219+
1220+
/* We get here if `buf' contains valid data. */
1221+
strcpy(dir->dd_dir.d_name, buf.cFileName);
1222+
++dir->dd_stat;
1223+
1224+
/* Set file type, based on WIN32_FIND_DATA */
1225+
mdir->dd_dir.d_type = 0;
1226+
if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1227+
mdir->dd_dir.d_type |= DT_DIR;
1228+
else
1229+
mdir->dd_dir.d_type |= DT_REG;
1230+
1231+
return (struct dirent*)&dir->dd_dir;
1232+
}
1233+
#endif // !NO_MINGW_REPLACE_READDIR

compat/mingw.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,32 @@ int main(int argc, const char **argv) \
235235
return mingw_main(argc, argv); \
236236
} \
237237
static int mingw_main(c,v)
238+
239+
#ifndef NO_MINGW_REPLACE_READDIR
240+
/*
241+
* A replacement of readdir, to ensure that it reads the file type at
242+
* the same time. This avoid extra unneeded lstats in git on MinGW
243+
*/
244+
#undef DT_UNKNOWN
245+
#undef DT_DIR
246+
#undef DT_REG
247+
#undef DT_LNK
248+
#define DT_UNKNOWN 0
249+
#define DT_DIR 1
250+
#define DT_REG 2
251+
#define DT_LNK 3
252+
253+
struct mingw_dirent
254+
{
255+
long d_ino; /* Always zero. */
256+
union {
257+
unsigned short d_reclen; /* Always zero. */
258+
unsigned char d_type; /* Reimplementation adds this */
259+
};
260+
unsigned short d_namlen; /* Length of name in d_name. */
261+
char d_name[FILENAME_MAX]; /* File name. */
262+
};
263+
#define dirent mingw_dirent
264+
#define readdir(x) mingw_readdir(x)
265+
struct dirent *mingw_readdir(DIR *dir);
266+
#endif // !NO_MINGW_REPLACE_READDIR

0 commit comments

Comments
 (0)