[libc++] Use __int128_t to represent file_time_type.
Summary:
The ``file_time_type`` time point is used to represent the write times for files.
Its job is to act as part of a C++ wrapper for less ideal system interfaces. The
underlying filesystem uses the ``timespec`` struct for the same purpose.
However, the initial implementation of ``file_time_type`` could not represent
either the range or resolution of ``timespec``, making it unsuitable. Fixing
this requires an implementation which uses more than 64 bits to store the
time point.
I primarily considered two solutions: Using ``__int128_t`` and using a
arithmetic emulation of ``timespec``. Each has its pros and cons, and both
come with more than one complication.
However, after a lot of consideration, I decided on using `__int128_t`. This patch implements that change.
Please see the [FileTimeType Design Document](http://libcxx.llvm.org/docs/DesignDocs/FileTimeType.html) for more information.
Reviewers: mclow.lists, ldionne, joerg, arthur.j.odwyer, EricWF
Reviewed By: EricWF
Subscribers: christof, K-ballo, cfe-commits, BillyONeal
Differential Revision: https://reviews.llvm.org/D49774
llvm-svn: 337960
diff --git a/libcxx/src/experimental/filesystem/filesystem_common.h b/libcxx/src/experimental/filesystem/filesystem_common.h
index 104d0aa..7be1674 100644
--- a/libcxx/src/experimental/filesystem/filesystem_common.h
+++ b/libcxx/src/experimental/filesystem/filesystem_common.h
@@ -23,33 +23,17 @@
#include <experimental/filesystem>
-#if (__APPLE__)
-#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101300
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 110000
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 110000
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 40000
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
-#else
+#include "../../include/apple_availability.h"
+
+#if !defined(__APPLE__)
// We can use the presence of UTIME_OMIT to detect platforms that provide
// utimensat.
#if defined(UTIME_OMIT)
-#define _LIBCXX_USE_UTIMENSAT
+#define _LIBCPP_USE_UTIMENSAT
#endif
-#endif // __APPLE__
+#endif
-#if !defined(_LIBCXX_USE_UTIMENSAT)
+#if !defined(_LIBCPP_USE_UTIMENSAT)
#include <sys/time.h> // for ::utimes as used in __last_write_time
#endif
@@ -212,76 +196,119 @@
ErrorHandler& operator=(ErrorHandler const&) = delete;
};
-namespace time_util {
+using chrono::duration;
+using chrono::duration_cast;
-using namespace chrono;
+using TimeSpec = struct ::timespec;
+using StatT = struct ::stat;
-template <class FileTimeT,
+template <class FileTimeT, class TimeT,
bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
-struct fs_time_util_base {
- static constexpr seconds::rep max_seconds =
- duration_cast<seconds>(FileTimeT::duration::max()).count();
+struct time_util_base {
+ using rep = typename FileTimeT::rep;
+ using fs_duration = typename FileTimeT::duration;
+ using fs_seconds = duration<rep>;
+ using fs_nanoseconds = duration<rep, nano>;
+ using fs_microseconds = duration<rep, micro>;
- static constexpr nanoseconds::rep max_nsec =
- duration_cast<nanoseconds>(FileTimeT::duration::max() -
- seconds(max_seconds))
+ static constexpr rep max_seconds =
+ duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
+
+ static constexpr rep max_nsec =
+ duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
+ fs_seconds(max_seconds))
.count();
- static constexpr seconds::rep min_seconds =
- duration_cast<seconds>(FileTimeT::duration::min()).count();
+ static constexpr rep min_seconds =
+ duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
- static constexpr nanoseconds::rep min_nsec_timespec =
- duration_cast<nanoseconds>(
- (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
+ static constexpr rep min_nsec_timespec =
+ duration_cast<fs_nanoseconds>(
+ (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
+ fs_seconds(1))
.count();
+private:
+#if _LIBCPP_STD_VER > 11
+ static constexpr fs_duration get_min_nsecs() {
+ return duration_cast<fs_duration>(
+ fs_nanoseconds(min_nsec_timespec) -
+ duration_cast<fs_nanoseconds>(fs_seconds(1)));
+ }
// 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)) ==
+ static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
FileTimeT::duration::min(),
- "");
+ "value doesn't roundtrip");
+
+ static constexpr bool check_range() {
+ // This kinda sucks, but it's what happens when we don't have __int128_t.
+ if (sizeof(TimeT) == sizeof(rep)) {
+ typedef duration<long long, ratio<3600 * 24 * 365> > Years;
+ return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
+ duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
+ }
+ return max_seconds >= numeric_limits<TimeT>::max() &&
+ min_seconds <= numeric_limits<TimeT>::min();
+ }
+ static_assert(check_range(), "the representable range is unacceptable small");
+#endif
};
-template <class FileTimeT>
-struct fs_time_util_base<FileTimeT, true> {
- static const long long max_seconds;
- static const long long max_nsec;
- static const long long min_seconds;
- static const long long min_nsec_timespec;
+template <class FileTimeT, class TimeT>
+struct time_util_base<FileTimeT, TimeT, true> {
+ using rep = typename FileTimeT::rep;
+ using fs_duration = typename FileTimeT::duration;
+ using fs_seconds = duration<rep>;
+ using fs_nanoseconds = duration<rep, nano>;
+ using fs_microseconds = duration<rep, micro>;
+
+ static const rep max_seconds;
+ static const rep max_nsec;
+ static const rep min_seconds;
+ static const rep min_nsec_timespec;
};
-template <class FileTimeT>
-const long long fs_time_util_base<FileTimeT, true>::max_seconds =
- duration_cast<seconds>(FileTimeT::duration::max()).count();
+template <class FileTimeT, class TimeT>
+const typename FileTimeT::rep
+ time_util_base<FileTimeT, TimeT, true>::max_seconds =
+ duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
-template <class FileTimeT>
-const long long fs_time_util_base<FileTimeT, true>::max_nsec =
- duration_cast<nanoseconds>(FileTimeT::duration::max() -
- seconds(max_seconds))
+template <class FileTimeT, class TimeT>
+const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
+ duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
+ fs_seconds(max_seconds))
.count();
-template <class FileTimeT>
-const long long fs_time_util_base<FileTimeT, true>::min_seconds =
- duration_cast<seconds>(FileTimeT::duration::min()).count();
+template <class FileTimeT, class TimeT>
+const typename FileTimeT::rep
+ time_util_base<FileTimeT, TimeT, true>::min_seconds =
+ duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
-template <class FileTimeT>
-const long long fs_time_util_base<FileTimeT, true>::min_nsec_timespec =
- duration_cast<nanoseconds>(
- (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
- .count();
+template <class FileTimeT, class TimeT>
+const typename FileTimeT::rep
+ time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
+ duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
+ fs_seconds(min_seconds)) +
+ fs_seconds(1))
+ .count();
template <class FileTimeT, class TimeT, class TimeSpecT>
-struct fs_time_util : fs_time_util_base<FileTimeT> {
- using Base = fs_time_util_base<FileTimeT>;
+struct time_util : time_util_base<FileTimeT, TimeT> {
+ using Base = time_util_base<FileTimeT, TimeT>;
using Base::max_nsec;
using Base::max_seconds;
using Base::min_nsec_timespec;
using Base::min_seconds;
+ using typename Base::fs_duration;
+ using typename Base::fs_microseconds;
+ using typename Base::fs_nanoseconds;
+ using typename Base::fs_seconds;
+
public:
template <class CType, class ChronoType>
- static bool checked_set(CType* out, ChronoType time) {
+ static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
+ ChronoType time) {
using Lim = numeric_limits<CType>;
if (time > Lim::max() || time < Lim::min())
return false;
@@ -291,21 +318,21 @@
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
if (tm.tv_sec >= 0) {
- return (tm.tv_sec < max_seconds) ||
+ 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);
+ return tm.tv_sec >= min_seconds;
}
}
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
- auto secs = duration_cast<seconds>(tm.time_since_epoch());
- auto nsecs = duration_cast<nanoseconds>(tm.time_since_epoch() - secs);
+ auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
+ auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
if (nsecs.count() < 0) {
- secs = secs + seconds(1);
- nsecs = nsecs + seconds(1);
+ secs = secs + fs_seconds(1);
+ nsecs = nsecs + fs_seconds(1);
}
using TLim = numeric_limits<TimeT>;
if (secs.count() >= 0)
@@ -314,49 +341,45 @@
}
static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
- convert_timespec(TimeSpecT 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 FileTimeT(Dur);
- } else if (duration_cast<microseconds>(nanoseconds(tm.tv_nsec)).count() ==
- 0) {
- return FileTimeT(seconds(tm.tv_sec));
+ convert_from_timespec(TimeSpecT tm) {
+ if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
+ return FileTimeT(fs_seconds(tm.tv_sec) +
+ duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
} 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;
+ auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
+ fs_nanoseconds(tm.tv_nsec));
+ auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
return FileTimeT(Dur);
}
}
- template <class SubSecDurT, class SubSecT>
- static bool set_times_checked(TimeT* sec_out, SubSecT* subsec_out,
- FileTimeT tp) {
+ template <class SubSecT>
+ static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
+ set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
auto dur = tp.time_since_epoch();
- auto sec_dur = duration_cast<seconds>(dur);
- auto subsec_dur = duration_cast<SubSecDurT>(dur - sec_dur);
+ auto sec_dur = duration_cast<fs_seconds>(dur);
+ auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
// The tv_nsec and tv_usec fields must not be negative so adjust accordingly
if (subsec_dur.count() < 0) {
if (sec_dur.count() > min_seconds) {
- sec_dur -= seconds(1);
- subsec_dur += seconds(1);
+ sec_dur -= fs_seconds(1);
+ subsec_dur += fs_seconds(1);
} else {
- subsec_dur = SubSecDurT::zero();
+ subsec_dur = fs_nanoseconds::zero();
}
}
return checked_set(sec_out, sec_dur.count()) &&
checked_set(subsec_out, subsec_dur.count());
}
+ static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
+ FileTimeT tp) {
+ if (!is_representable(tp))
+ return false;
+ return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
+ }
};
-} // namespace time_util
-
-
-using TimeSpec = struct ::timespec;
-using StatT = struct ::stat;
-
-using FSTime = time_util::fs_time_util<file_time_type, time_t, struct timespec>;
+using fs_time = time_util<file_time_type, time_t, TimeSpec>;
#if defined(__APPLE__)
TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
@@ -366,20 +389,18 @@
TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
#endif
-#if !defined(_LIBCXX_USE_UTIMENSAT)
-using TimeStruct = struct ::timeval;
-using TimeStructArray = TimeStruct[2];
+bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
+ error_code& ec) {
+#if !defined(_LIBCPP_USE_UTIMENSAT)
+ using namespace chrono;
+ auto Convert = [](long nsec) {
+ return duration_cast<microseconds>(nanoseconds(nsec)).count();
+ };
+ struct ::timeval ConvertedTS[2] = {{TS[0].tv_sec, Convert(TS[0].tv_nsec)},
+ {TS[1].tv_sec, Convert(TS[1].tv_nsec)}};
+ if (::utimes(p.c_str(), ConvertedTS) == -1)
#else
-using TimeStruct = TimeSpec;
-using TimeStructArray = TimeStruct[2];
-#endif
-
-bool SetFileTimes(const path& p, TimeStructArray const& TS,
- error_code& ec) {
-#if !defined(_LIBCXX_USE_UTIMENSAT)
- if (::utimes(p.c_str(), TS) == -1)
-#else
- if (::utimensat(AT_FDCWD, p.c_str(), TS, 0) == -1)
+ if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1)
#endif
{
ec = capture_errno();
@@ -388,25 +409,9 @@
return false;
}
-void SetTimeStructTo(TimeStruct& TS, TimeSpec ToTS) {
- using namespace chrono;
- TS.tv_sec = ToTS.tv_sec;
-#if !defined(_LIBCXX_USE_UTIMENSAT)
- TS.tv_usec = duration_cast<microseconds>(nanoseconds(ToTS.tv_nsec)).count();
-#else
- TS.tv_nsec = ToTS.tv_nsec;
-#endif
-}
-
-bool SetTimeStructTo(TimeStruct& TS, file_time_type NewTime) {
- using namespace chrono;
-#if !defined(_LIBCXX_USE_UTIMENSAT)
- return !FSTime::set_times_checked<microseconds>(&TS.tv_sec, &TS.tv_usec,
- NewTime);
-#else
- return !FSTime::set_times_checked<nanoseconds>(&TS.tv_sec, &TS.tv_nsec,
- NewTime);
-#endif
+bool set_time_spec_to(TimeSpec& TS, file_time_type NewTime) {
+ return !fs_time::set_times_checked(
+ &TS.tv_sec, &TS.tv_nsec, NewTime);
}
} // namespace