blob: a60fdef5f0d9a1ca61bc274a55fd5ad3755ba7ed [file] [log] [blame]
Eric Fiselier433c2f02017-07-08 04:18:41 +00001//===----------------------------------------------------------------------===////
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
26namespace time_detail { namespace {
27
28using namespace chrono;
29
30template <class FileTimeT,
31 bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
32struct 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
57template <class FileTimeT>
58struct 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
65template <class FileTimeT>
66const long long fs_time_util_base<FileTimeT, true>::max_seconds =
67 duration_cast<seconds>(FileTimeT::duration::max()).count();
68
69template <class FileTimeT>
70const long long fs_time_util_base<FileTimeT, true>::max_nsec =
71 duration_cast<nanoseconds>(FileTimeT::duration::max() -
72 seconds(max_seconds))
73 .count();
74
75template <class FileTimeT>
76const long long fs_time_util_base<FileTimeT, true>::min_seconds =
77 duration_cast<seconds>(FileTimeT::duration::min()).count();
78
79template <class FileTimeT>
80const 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
86template <class FileTimeT, class TimeT, class TimeSpecT>
87struct 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
94public:
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
169using time_detail::fs_time_util;
170
171_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
172
173#endif // FILESYSTEM_TIME_HELPER_H