[libcxx] r283712 - Remove use of int128_t inside the filesystem implementation

Eric Fiselier via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 9 21:22:59 PDT 2016


Author: ericwf
Date: Sun Oct  9 23:22:58 2016
New Revision: 283712

URL: http://llvm.org/viewvc/llvm-project?rev=283712&view=rev
Log:
Remove use of int128_t inside the filesystem implementation

Modified:
    libcxx/trunk/src/experimental/filesystem/operations.cpp

Modified: libcxx/trunk/src/experimental/filesystem/operations.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/src/experimental/filesystem/operations.cpp?rev=283712&r1=283711&r2=283712&view=diff
==============================================================================
--- libcxx/trunk/src/experimental/filesystem/operations.cpp (original)
+++ libcxx/trunk/src/experimental/filesystem/operations.cpp Sun Oct  9 23:22:58 2016
@@ -488,6 +488,8 @@ bool __fs_is_empty(const path& p, std::e
 
 namespace detail { namespace {
 
+using namespace std::chrono;
+
 template <class CType, class ChronoType>
 bool checked_set(CType* out, ChronoType time) {
     using Lim = numeric_limits<CType>;
@@ -497,8 +499,120 @@ bool checked_set(CType* out, ChronoType
     return true;
 }
 
-constexpr long long min_seconds = file_time_type::duration::min().count()
-    / file_time_type::period::den;
+using TimeSpec = struct ::timespec;
+using StatT = struct ::stat;
+
+#if defined(__APPLE__)
+TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
+TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
+#else
+TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
+__attribute__((unused)) // Suppress warning
+TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
+#endif
+
+constexpr auto max_seconds = duration_cast<seconds>(
+    file_time_type::duration::max()).count();
+
+constexpr auto max_nsec = duration_cast<nanoseconds>(
+    file_time_type::duration::max() - seconds(max_seconds)).count();
+
+constexpr auto min_seconds = duration_cast<seconds>(
+    file_time_type::duration::min()).count();
+
+constexpr auto min_nsec_timespec = duration_cast<nanoseconds>(
+    (file_time_type::duration::min() - seconds(min_seconds)) + seconds(1)).count();
+
+// Static assert that these values properly round trip.
+static_assert((seconds(min_seconds) + duration_cast<microseconds>(nanoseconds(min_nsec_timespec)))
+                  - duration_cast<microseconds>(seconds(1))
+                  == file_time_type::duration::min(), "");
+
+constexpr auto max_time_t = numeric_limits<time_t>::max();
+constexpr auto min_time_t = numeric_limits<time_t>::min();
+
+#if !defined(__LP64__) && defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
+#endif
+
+constexpr bool is_representable(TimeSpec const& tm) {
+  if (tm.tv_sec >= 0) {
+    return (tm.tv_sec < max_seconds) ||
+        (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
+  } else if (tm.tv_sec == (min_seconds - 1)) {
+     return tm.tv_nsec >= min_nsec_timespec;
+  } else {
+    return (tm.tv_sec >= min_seconds);
+  }
+}
+
+#if defined(__LP64__)
+static_assert(is_representable({max_seconds, max_nsec}), "");
+static_assert(!is_representable({max_seconds + 1, 0}), "");
+static_assert(!is_representable({max_seconds, max_nsec + 1}), "");
+static_assert(!is_representable({max_time_t, 0}), "");
+static_assert(is_representable({min_seconds, 0}), "");
+static_assert(is_representable({min_seconds - 1, min_nsec_timespec}), "");
+static_assert(is_representable({min_seconds - 1, min_nsec_timespec + 1}), "");
+static_assert(!is_representable({min_seconds - 1, min_nsec_timespec - 1}), "");
+static_assert(!is_representable({min_time_t, 999999999}), "");
+#else
+static_assert(is_representable({max_time_t, 999999999}), "");
+static_assert(is_representable({max_time_t, 1000000000}), "");
+static_assert(is_representable({min_time_t, 0}), "");
+#endif
+
+constexpr bool is_representable(file_time_type const& tm) {
+  auto secs = duration_cast<seconds>(tm.time_since_epoch());
+  auto nsecs = duration_cast<nanoseconds>(tm.time_since_epoch() - secs);
+  if (nsecs.count() < 0) {
+    secs = secs +  seconds(1);
+    nsecs = nsecs + seconds(1);
+  }
+  using TLim = numeric_limits<time_t>;
+  if (secs.count() >= 0)
+    return secs.count() <= TLim::max();
+  return secs.count() >= TLim::min();
+}
+#if defined(__LP64__)
+static_assert(is_representable(file_time_type::max()), "");
+static_assert(is_representable(file_time_type::min()), "");
+#else
+static_assert(!is_representable(file_time_type::max()), "");
+static_assert(!is_representable(file_time_type::min()), "");
+static_assert(is_representable(file_time_type(seconds(max_time_t))), "");
+static_assert(is_representable(file_time_type(seconds(min_time_t))), "");
+#endif
+
+template <long long V> struct Dummy;
+constexpr file_time_type convert_timespec(TimeSpec const& tm) {
+  auto adj_msec = duration_cast<microseconds>(nanoseconds(tm.tv_nsec));
+  if (tm.tv_sec >= 0) {
+    auto Dur = seconds(tm.tv_sec) + microseconds(adj_msec);
+    return file_time_type(Dur);
+  } else if (duration_cast<microseconds>(nanoseconds(tm.tv_nsec)).count() == 0) {
+    return file_time_type(seconds(tm.tv_sec));
+  } else { // tm.tv_sec < 0
+    auto adj_subsec = duration_cast<microseconds>(seconds(1) - nanoseconds(tm.tv_nsec));
+    auto Dur = seconds(tm.tv_sec + 1) - adj_subsec;
+    return file_time_type(Dur);
+  }
+}
+#if defined(__LP64__)
+static_assert(convert_timespec({max_seconds, max_nsec}) == file_time_type::max(), "");
+static_assert(convert_timespec({max_seconds, max_nsec - 1}) < file_time_type::max(), "");
+static_assert(convert_timespec({max_seconds - 1, 999999999}) < file_time_type::max(), "");
+static_assert(convert_timespec({min_seconds - 1, min_nsec_timespec}) == file_time_type::min(), "");
+static_assert(convert_timespec({min_seconds - 1, min_nsec_timespec + 1}) > file_time_type::min(), "");
+static_assert(convert_timespec({min_seconds , 0}) > file_time_type::min(), "");
+#else
+// FIXME add tests for 32 bit builds
+#endif
+
+#if !defined(__LP64__) && defined(__clang__)
+#pragma clang diagnostic pop
+#endif
 
 template <class SubSecDurT, class SubSecT>
 bool set_times_checked(time_t* sec_out, SubSecT* subsec_out, file_time_type tp) {
@@ -519,21 +633,8 @@ bool set_times_checked(time_t* sec_out,
         && checked_set(subsec_out, subsec_dur.count());
 }
 
-using TimeSpec = struct ::timespec;
-using StatT = struct ::stat;
-
-#if defined(__APPLE__)
-TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
-TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
-#else
-TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
-__attribute__((unused)) // Suppress warning
-TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
-#endif
-
 }} // end namespace detail
 
-
 file_time_type __last_write_time(const path& p, std::error_code *ec)
 {
     using namespace ::std::chrono;
@@ -546,25 +647,12 @@ file_time_type __last_write_time(const p
     }
     if (ec) ec->clear();
     auto ts = detail::extract_mtime(st);
-#ifndef _LIBCPP_HAS_NO_INT128
-    using IntMax = __int128_t;
-    // FIXME: The value may not be representable as file_time_type. Fix
-    // file_time_type so it can represent all possible values returned by the
-    // filesystem. For now we do the calculation with the largest possible types
-    // and then truncate, this prevents signed integer overflow bugs.
-    const auto NsDur = duration<IntMax, nano>(ts.tv_nsec) + seconds(ts.tv_sec);
-    if (NsDur > file_time_type::max().time_since_epoch() ||
-        NsDur < file_time_type::min().time_since_epoch()) {
+    if (!detail::is_representable(ts)) {
         set_or_throw(error_code(EOVERFLOW, generic_category()), ec,
                      "last_write_time", p);
         return file_time_type::min();
     }
-    return file_time_type(duration_cast<file_time_type::duration>(NsDur));
-#else
-    // FIXME the under/overflow check done above overflows if we don't have
-    // a 128 bit integer type.
-    return file_time_type::clock::from_time_t(ts.tv_sec);
-#endif
+    return detail::convert_timespec(ts);
 }
 
 void __last_write_time(const path& p, file_time_type new_time,




More information about the cfe-commits mailing list