From 93c78361e30e33f950eef754742b236251e2c81e Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Tue, 20 Dec 2011 12:13:11 -0800 Subject: 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... --- src/disk_interface.cc | 33 +++++++++++++++++++++++++++------ src/disk_interface_test.cc | 9 +++++++-- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index d82b544..b60cd6f 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -19,6 +19,10 @@ #include #include +#ifdef WIN32 +#include +#endif + #include "util.h" namespace { @@ -62,17 +66,34 @@ bool DiskInterface::MakeDirs(const std::string& path) { // RealDiskInterface ----------------------------------------------------------- int RealDiskInterface::Stat(const std::string& path) { +#ifdef WIN32 + WIN32_FILE_ATTRIBUTE_DATA attrs; + if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &attrs)) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + return 0; + Error("GetFileAttributesEx(%s): %s", path.c_str(), + GetLastErrorString().c_str()); + return -1; + } + const FILETIME& filetime = attrs.ftLastWriteTime; + // FILETIME is in 100-nanosecond increments since the Windows epoch. + // We don't much care about epoch correctness but we do want the + // resulting value to fit in an integer. + uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) | + ((uint64_t)filetime.dwLowDateTime); + mtime /= 1000000000LL / 100; // 100ns -> s. + mtime -= 12622770400LL; // 1600 epoch -> 2000 epoch (subtract 400 years). + return mtime; +#else struct stat st; if (stat(path.c_str(), &st) < 0) { - if (errno == ENOENT) { + if (errno == ENOENT) return 0; - } else { - Error("stat(%s): %s", path.c_str(), strerror(errno)); - return -1; - } + Error("stat(%s): %s", path.c_str(), strerror(errno)); + return -1; } - return st.st_mtime; +#endif } bool RealDiskInterface::MakeDir(const std::string& path) { diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc index 0ca34ad..d0794fd 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -107,16 +107,21 @@ class DiskInterfaceTest : public testing::Test { RealDiskInterface disk_; }; -TEST_F(DiskInterfaceTest, Stat) { +TEST_F(DiskInterfaceTest, StatMissingFile) { EXPECT_EQ(0, disk_.Stat("nosuchfile")); +} +TEST_F(DiskInterfaceTest, StatBadPath) { #ifdef _WIN32 - // TODO: find something that stat fails on for Windows. + string bad_path = "cc:\\foo"; + EXPECT_EQ(-1, disk_.Stat(bad_path)); #else string too_long_name(512, 'x'); EXPECT_EQ(-1, disk_.Stat(too_long_name)); #endif +} +TEST_F(DiskInterfaceTest, StatExistingFile) { #ifdef _WIN32 ASSERT_EQ(0, system("cmd.exe /c echo hi > file")); #else -- cgit v0.12