[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 git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@337960 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -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
|
||||
#endif // __APPLE__
|
||||
|
||||
#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 @@ private:
|
||||
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 @@ public:
|
||||
|
||||
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 @@ public:
|
||||
}
|
||||
|
||||
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_mtime(StatT const& st) { return st.st_mtim; }
|
||||
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 @@ bool SetFileTimes(const path& p, TimeStructArray const& TS,
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user