windows: use GetFileAttributesEx instead of stat
authorEvan Martin <martine@danga.com>
Tue, 20 Dec 2011 20:13:11 +0000 (12:13 -0800)
committerEvan Martin <martine@danga.com>
Tue, 20 Dec 2011 20:13:11 +0000 (12:13 -0800)
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
src/disk_interface_test.cc

index d82b544..b60cd6f 100644 (file)
 #include <string.h>
 #include <sys/stat.h>
 
+#ifdef WIN32
+#include <windows.h>
+#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) {
index 0ca34ad..d0794fd 100644 (file)
@@ -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