• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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