Home | History | Annotate | Download | only in filesystem
      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