1 //===----------------------------------------------------------------------===//// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is dual licensed under the MIT and the University of Illinois Open 6 // Source Licenses. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===//// 9 10 #ifndef FILESYSTEM_TIME_HELPER_H 11 #define FILESYSTEM_TIME_HELPER_H 12 13 #include "experimental/__config" 14 #include "chrono" 15 #include "cstdlib" 16 #include "climits" 17 18 #include <unistd.h> 19 #include <sys/stat.h> 20 #if !defined(UTIME_OMIT) 21 #include <sys/time.h> // for ::utimes as used in __last_write_time 22 #endif 23 24 _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM 25 26 namespace time_detail { namespace { 27 28 using namespace chrono; 29 30 template <class FileTimeT, 31 bool IsFloat = is_floating_point<typename FileTimeT::rep>::value> 32 struct fs_time_util_base { 33 static constexpr auto max_seconds = 34 duration_cast<seconds>(FileTimeT::duration::max()).count(); 35 36 static constexpr auto max_nsec = 37 duration_cast<nanoseconds>(FileTimeT::duration::max() - 38 seconds(max_seconds)) 39 .count(); 40 41 static constexpr auto min_seconds = 42 duration_cast<seconds>(FileTimeT::duration::min()).count(); 43 44 static constexpr auto min_nsec_timespec = 45 duration_cast<nanoseconds>( 46 (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1)) 47 .count(); 48 49 // Static assert that these values properly round trip. 50 static_assert((seconds(min_seconds) + 51 duration_cast<microseconds>(nanoseconds(min_nsec_timespec))) - 52 duration_cast<microseconds>(seconds(1)) == 53 FileTimeT::duration::min(), 54 ""); 55 }; 56 57 template <class FileTimeT> 58 struct fs_time_util_base<FileTimeT, true> { 59 static const long long max_seconds; 60 static const long long max_nsec; 61 static const long long min_seconds; 62 static const long long min_nsec_timespec; 63 }; 64 65 template <class FileTimeT> 66 const long long fs_time_util_base<FileTimeT, true>::max_seconds = 67 duration_cast<seconds>(FileTimeT::duration::max()).count(); 68 69 template <class FileTimeT> 70 const long long fs_time_util_base<FileTimeT, true>::max_nsec = 71 duration_cast<nanoseconds>(FileTimeT::duration::max() - 72 seconds(max_seconds)) 73 .count(); 74 75 template <class FileTimeT> 76 const long long fs_time_util_base<FileTimeT, true>::min_seconds = 77 duration_cast<seconds>(FileTimeT::duration::min()).count(); 78 79 template <class FileTimeT> 80 const long long fs_time_util_base<FileTimeT, true>::min_nsec_timespec = 81 duration_cast<nanoseconds>((FileTimeT::duration::min() - 82 seconds(min_seconds)) + 83 seconds(1)) 84 .count(); 85 86 template <class FileTimeT, class TimeT, class TimeSpecT> 87 struct fs_time_util : fs_time_util_base<FileTimeT> { 88 using Base = fs_time_util_base<FileTimeT>; 89 using Base::max_nsec; 90 using Base::max_seconds; 91 using Base::min_nsec_timespec; 92 using Base::min_seconds; 93 94 public: 95 template <class CType, class ChronoType> 96 static bool checked_set(CType* out, ChronoType time) { 97 using Lim = numeric_limits<CType>; 98 if (time > Lim::max() || time < Lim::min()) 99 return false; 100 *out = static_cast<CType>(time); 101 return true; 102 } 103 104 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) { 105 if (tm.tv_sec >= 0) { 106 return (tm.tv_sec < max_seconds) || 107 (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec); 108 } else if (tm.tv_sec == (min_seconds - 1)) { 109 return tm.tv_nsec >= min_nsec_timespec; 110 } else { 111 return (tm.tv_sec >= min_seconds); 112 } 113 } 114 115 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) { 116 auto secs = duration_cast<seconds>(tm.time_since_epoch()); 117 auto nsecs = duration_cast<nanoseconds>(tm.time_since_epoch() - secs); 118 if (nsecs.count() < 0) { 119 secs = secs + seconds(1); 120 nsecs = nsecs + seconds(1); 121 } 122 using TLim = numeric_limits<TimeT>; 123 if (secs.count() >= 0) 124 return secs.count() <= TLim::max(); 125 return secs.count() >= TLim::min(); 126 } 127 128 static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT 129 convert_timespec(TimeSpecT tm) { 130 auto adj_msec = duration_cast<microseconds>(nanoseconds(tm.tv_nsec)); 131 if (tm.tv_sec >= 0) { 132 auto Dur = seconds(tm.tv_sec) + microseconds(adj_msec); 133 return FileTimeT(Dur); 134 } else if (duration_cast<microseconds>(nanoseconds(tm.tv_nsec)).count() == 135 0) { 136 return FileTimeT(seconds(tm.tv_sec)); 137 } else { // tm.tv_sec < 0 138 auto adj_subsec = 139 duration_cast<microseconds>(seconds(1) - nanoseconds(tm.tv_nsec)); 140 auto Dur = seconds(tm.tv_sec + 1) - adj_subsec; 141 return FileTimeT(Dur); 142 } 143 } 144 145 template <class SubSecDurT, class SubSecT> 146 static bool set_times_checked(TimeT* sec_out, SubSecT* subsec_out, 147 FileTimeT tp) { 148 using namespace chrono; 149 auto dur = tp.time_since_epoch(); 150 auto sec_dur = duration_cast<seconds>(dur); 151 auto subsec_dur = duration_cast<SubSecDurT>(dur - sec_dur); 152 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly 153 if (subsec_dur.count() < 0) { 154 if (sec_dur.count() > min_seconds) { 155 sec_dur -= seconds(1); 156 subsec_dur += seconds(1); 157 } else { 158 subsec_dur = SubSecDurT::zero(); 159 } 160 } 161 return checked_set(sec_out, sec_dur.count()) && 162 checked_set(subsec_out, subsec_dur.count()); 163 } 164 }; 165 166 } // end namespace 167 } // end namespace time_detail 168 169 using time_detail::fs_time_util; 170 171 _LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM 172 173 #endif // FILESYSTEM_TIME_HELPER_H 174