Skip to content
This repository was archived by the owner on Jun 15, 2023. It is now read-only.

Commit 93c7836

Browse files
committed
windows: use GetFileAttributesEx instead of stat
From a Hacker News comment: "Recent finding, that sped up our systems from 15->3sec on 300,000+ files filestamp check was to move from _stat to GetFileAttributesEx." I do recall reading that calls to stat() on Windows were one of the potential reasons Subversion is so slow on Windows...
1 parent bf9116f commit 93c7836

File tree

2 files changed

+34
-8
lines changed

2 files changed

+34
-8
lines changed

src/disk_interface.cc

+27-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
#include <string.h>
2020
#include <sys/stat.h>
2121

22+
#ifdef WIN32
23+
#include <windows.h>
24+
#endif
25+
2226
#include "util.h"
2327

2428
namespace {
@@ -62,17 +66,34 @@ bool DiskInterface::MakeDirs(const std::string& path) {
6266
// RealDiskInterface -----------------------------------------------------------
6367

6468
int RealDiskInterface::Stat(const std::string& path) {
69+
#ifdef WIN32
70+
WIN32_FILE_ATTRIBUTE_DATA attrs;
71+
if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &attrs)) {
72+
if (GetLastError() == ERROR_FILE_NOT_FOUND)
73+
return 0;
74+
Error("GetFileAttributesEx(%s): %s", path.c_str(),
75+
GetLastErrorString().c_str());
76+
return -1;
77+
}
78+
const FILETIME& filetime = attrs.ftLastWriteTime;
79+
// FILETIME is in 100-nanosecond increments since the Windows epoch.
80+
// We don't much care about epoch correctness but we do want the
81+
// resulting value to fit in an integer.
82+
uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) |
83+
((uint64_t)filetime.dwLowDateTime);
84+
mtime /= 1000000000LL / 100; // 100ns -> s.
85+
mtime -= 12622770400LL; // 1600 epoch -> 2000 epoch (subtract 400 years).
86+
return mtime;
87+
#else
6588
struct stat st;
6689
if (stat(path.c_str(), &st) < 0) {
67-
if (errno == ENOENT) {
90+
if (errno == ENOENT)
6891
return 0;
69-
} else {
70-
Error("stat(%s): %s", path.c_str(), strerror(errno));
71-
return -1;
72-
}
92+
Error("stat(%s): %s", path.c_str(), strerror(errno));
93+
return -1;
7394
}
74-
7595
return st.st_mtime;
96+
#endif
7697
}
7798

7899
bool RealDiskInterface::MakeDir(const std::string& path) {

src/disk_interface_test.cc

+7-2
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,21 @@ class DiskInterfaceTest : public testing::Test {
107107
RealDiskInterface disk_;
108108
};
109109

110-
TEST_F(DiskInterfaceTest, Stat) {
110+
TEST_F(DiskInterfaceTest, StatMissingFile) {
111111
EXPECT_EQ(0, disk_.Stat("nosuchfile"));
112+
}
112113

114+
TEST_F(DiskInterfaceTest, StatBadPath) {
113115
#ifdef _WIN32
114-
// TODO: find something that stat fails on for Windows.
116+
string bad_path = "cc:\\foo";
117+
EXPECT_EQ(-1, disk_.Stat(bad_path));
115118
#else
116119
string too_long_name(512, 'x');
117120
EXPECT_EQ(-1, disk_.Stat(too_long_name));
118121
#endif
122+
}
119123

124+
TEST_F(DiskInterfaceTest, StatExistingFile) {
120125
#ifdef _WIN32
121126
ASSERT_EQ(0, system("cmd.exe /c echo hi > file"));
122127
#else

0 commit comments

Comments
 (0)