1 | /* |
2 | * Copyright 2017-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | /** |
18 | * Conversions between std::chrono types and POSIX time types. |
19 | * |
20 | * These conversions will fail with a ConversionError if an overflow would |
21 | * occur performing the conversion. (e.g., if the input value cannot fit in |
22 | * the destination type). However they allow loss of precision (e.g., |
23 | * converting nanoseconds to a struct timeval which only has microsecond |
24 | * granularity, or a struct timespec to std::chrono::minutes). |
25 | */ |
26 | |
27 | #pragma once |
28 | |
29 | #include <chrono> |
30 | #include <type_traits> |
31 | |
32 | #include <folly/Conv.h> |
33 | #include <folly/Expected.h> |
34 | #include <folly/portability/SysTime.h> |
35 | #include <folly/portability/SysTypes.h> |
36 | |
37 | namespace folly { |
38 | namespace detail { |
39 | |
40 | template <typename T> |
41 | struct is_duration : std::false_type {}; |
42 | template <typename Rep, typename Period> |
43 | struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type {}; |
44 | template <typename T> |
45 | struct is_time_point : std::false_type {}; |
46 | template <typename Clock, typename Duration> |
47 | struct is_time_point<std::chrono::time_point<Clock, Duration>> |
48 | : std::true_type {}; |
49 | template <typename T> |
50 | struct is_std_chrono_type { |
51 | static constexpr bool value = |
52 | is_duration<T>::value || is_time_point<T>::value; |
53 | }; |
54 | template <typename T> |
55 | struct is_posix_time_type { |
56 | static constexpr bool value = std::is_same<T, struct timespec>::value || |
57 | std::is_same<T, struct timeval>::value; |
58 | }; |
59 | template <typename Tgt, typename Src> |
60 | struct is_chrono_conversion { |
61 | static constexpr bool value = |
62 | ((is_std_chrono_type<Tgt>::value && is_posix_time_type<Src>::value) || |
63 | (is_posix_time_type<Tgt>::value && is_std_chrono_type<Src>::value)); |
64 | }; |
65 | |
66 | /** |
67 | * This converts a number in some input type to time_t while ensuring that it |
68 | * fits in the range of numbers representable by time_t. |
69 | * |
70 | * This is similar to the normal folly::tryTo() behavior when converting |
71 | * arthmetic types to an integer type, except that it does not complain about |
72 | * floating point conversions losing precision. |
73 | */ |
74 | template <typename Src> |
75 | Expected<time_t, ConversionCode> chronoRangeCheck(Src value) { |
76 | if (value > std::numeric_limits<time_t>::max()) { |
77 | return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); |
78 | } |
79 | if (std::is_signed<Src>::value) { |
80 | if (value < std::numeric_limits<time_t>::lowest()) { |
81 | return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); |
82 | } |
83 | } |
84 | |
85 | return static_cast<time_t>(value); |
86 | } |
87 | |
88 | /** |
89 | * Convert a std::chrono::duration with second granularity to a pair of |
90 | * (seconds, subseconds) |
91 | * |
92 | * The SubsecondRatio template parameter specifies what type of subseconds to |
93 | * return. This must have a numerator of 1. |
94 | */ |
95 | template <typename SubsecondRatio, typename Rep> |
96 | static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime( |
97 | const std::chrono::duration<Rep, std::ratio<1, 1>>& duration) { |
98 | static_assert( |
99 | SubsecondRatio::num == 1, "subsecond numerator should always be 1" ); |
100 | |
101 | auto sec = chronoRangeCheck(duration.count()); |
102 | if (sec.hasError()) { |
103 | return makeUnexpected(sec.error()); |
104 | } |
105 | |
106 | time_t secValue = sec.value(); |
107 | long subsec = 0L; |
108 | if (std::is_floating_point<Rep>::value) { |
109 | auto fraction = (duration.count() - secValue); |
110 | subsec = static_cast<long>(fraction * SubsecondRatio::den); |
111 | if (duration.count() < 0 && fraction < 0) { |
112 | if (secValue == std::numeric_limits<time_t>::lowest()) { |
113 | return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); |
114 | } |
115 | secValue -= 1; |
116 | subsec += SubsecondRatio::den; |
117 | } |
118 | } |
119 | return std::pair<time_t, long>{secValue, subsec}; |
120 | } |
121 | |
122 | /** |
123 | * Convert a std::chrono::duration with subsecond granularity to a pair of |
124 | * (seconds, subseconds) |
125 | */ |
126 | template <typename SubsecondRatio, typename Rep, std::intmax_t Denominator> |
127 | static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime( |
128 | const std::chrono::duration<Rep, std::ratio<1, Denominator>>& duration) { |
129 | static_assert(Denominator != 1, "special case expecting den != 1" ); |
130 | static_assert( |
131 | SubsecondRatio::num == 1, "subsecond numerator should always be 1" ); |
132 | |
133 | auto sec = chronoRangeCheck(duration.count() / Denominator); |
134 | if (sec.hasError()) { |
135 | return makeUnexpected(sec.error()); |
136 | } |
137 | auto secTimeT = sec.value(); |
138 | |
139 | auto remainder = duration.count() - (secTimeT * Denominator); |
140 | auto subsec = (remainder * SubsecondRatio::den) / Denominator; |
141 | if (UNLIKELY(duration.count() < 0) && remainder != 0) { |
142 | if (secTimeT == std::numeric_limits<time_t>::lowest()) { |
143 | return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); |
144 | } |
145 | secTimeT -= 1; |
146 | subsec += SubsecondRatio::den; |
147 | } |
148 | |
149 | return std::pair<time_t, long>{secTimeT, subsec}; |
150 | } |
151 | |
152 | /** |
153 | * Convert a std::chrono::duration with coarser-than-second granularity to a |
154 | * pair of (seconds, subseconds) |
155 | */ |
156 | template <typename SubsecondRatio, typename Rep, std::intmax_t Numerator> |
157 | static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime( |
158 | const std::chrono::duration<Rep, std::ratio<Numerator, 1>>& duration) { |
159 | static_assert(Numerator != 1, "special case expecting num!=1" ); |
160 | static_assert( |
161 | SubsecondRatio::num == 1, "subsecond numerator should always be 1" ); |
162 | |
163 | constexpr auto maxValue = std::numeric_limits<time_t>::max() / Numerator; |
164 | constexpr auto minValue = std::numeric_limits<time_t>::lowest() / Numerator; |
165 | if (duration.count() > maxValue) { |
166 | return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); |
167 | } |
168 | if (duration.count() < minValue) { |
169 | return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); |
170 | } |
171 | |
172 | // Note that we can't use chronoRangeCheck() here since we have to check |
173 | // if (duration.count() * Numerator) would overflow (which we do above). |
174 | auto secOriginalRep = (duration.count() * Numerator); |
175 | auto sec = static_cast<time_t>(secOriginalRep); |
176 | |
177 | long subsec = 0L; |
178 | if (std::is_floating_point<Rep>::value) { |
179 | auto fraction = secOriginalRep - sec; |
180 | subsec = static_cast<long>(fraction * SubsecondRatio::den); |
181 | if (duration.count() < 0 && fraction < 0) { |
182 | if (sec == std::numeric_limits<time_t>::lowest()) { |
183 | return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); |
184 | } |
185 | sec -= 1; |
186 | subsec += SubsecondRatio::den; |
187 | } |
188 | } |
189 | return std::pair<time_t, long>{sec, subsec}; |
190 | } |
191 | |
192 | /* |
193 | * Helper classes for picking an intermediate duration type to use |
194 | * when doing conversions to/from durations where neither the numerator nor |
195 | * denominator are 1. |
196 | */ |
197 | template <typename T, bool IsFloatingPoint, bool IsSigned> |
198 | struct IntermediateTimeRep {}; |
199 | template <typename T, bool IsSigned> |
200 | struct IntermediateTimeRep<T, true, IsSigned> { |
201 | using type = T; |
202 | }; |
203 | template <typename T> |
204 | struct IntermediateTimeRep<T, false, true> { |
205 | using type = intmax_t; |
206 | }; |
207 | template <typename T> |
208 | struct IntermediateTimeRep<T, false, false> { |
209 | using type = uintmax_t; |
210 | }; |
211 | // For IntermediateDuration we always use 1 as the numerator, and the original |
212 | // Period denominator. This ensures that we do not lose precision when |
213 | // performing the conversion. |
214 | template <typename Rep, typename Period> |
215 | using IntermediateDuration = std::chrono::duration< |
216 | typename IntermediateTimeRep< |
217 | Rep, |
218 | std::is_floating_point<Rep>::value, |
219 | std::is_signed<Rep>::value>::type, |
220 | std::ratio<1, Period::den>>; |
221 | |
222 | /** |
223 | * Convert a std::chrono::duration to a pair of (seconds, subseconds) |
224 | * |
225 | * This overload is only used for unusual durations where neither the numerator |
226 | * nor denominator are 1. |
227 | */ |
228 | template <typename SubsecondRatio, typename Rep, typename Period> |
229 | Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime( |
230 | const std::chrono::duration<Rep, Period>& duration) { |
231 | static_assert(Period::num != 1, "should use special-case code when num==1" ); |
232 | static_assert(Period::den != 1, "should use special-case code when den==1" ); |
233 | static_assert( |
234 | SubsecondRatio::num == 1, "subsecond numerator should always be 1" ); |
235 | |
236 | // Perform this conversion by first converting to a duration where the |
237 | // numerator is 1, then convert to the output type. |
238 | using IntermediateType = IntermediateDuration<Rep, Period>; |
239 | using IntermediateRep = typename IntermediateType::rep; |
240 | |
241 | // Check to see if we would have overflow converting to the intermediate |
242 | // type. |
243 | constexpr auto maxInput = |
244 | std::numeric_limits<IntermediateRep>::max() / Period::num; |
245 | if (duration.count() > maxInput) { |
246 | return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); |
247 | } |
248 | constexpr auto minInput = |
249 | std::numeric_limits<IntermediateRep>::min() / Period::num; |
250 | if (duration.count() < minInput) { |
251 | return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); |
252 | } |
253 | auto intermediate = |
254 | IntermediateType{static_cast<IntermediateRep>(duration.count()) * |
255 | static_cast<IntermediateRep>(Period::num)}; |
256 | |
257 | return durationToPosixTime<SubsecondRatio>(intermediate); |
258 | } |
259 | |
260 | /** |
261 | * Check for overflow when converting to a duration type that is second |
262 | * granularity or finer (e.g., nanoseconds, milliseconds, seconds) |
263 | * |
264 | * This assumes the input is normalized, with subseconds >= 0 and subseconds |
265 | * less than 1 second. |
266 | */ |
267 | template <bool IsFloatingPoint> |
268 | struct CheckOverflowToDuration { |
269 | template < |
270 | typename Tgt, |
271 | typename SubsecondRatio, |
272 | typename Seconds, |
273 | typename Subseconds> |
274 | static ConversionCode check(Seconds seconds, Subseconds subseconds) { |
275 | static_assert( |
276 | Tgt::period::num == 1, |
277 | "this implementation should only be used for subsecond granularity " |
278 | "duration types" ); |
279 | static_assert( |
280 | !std::is_floating_point<typename Tgt::rep>::value, "incorrect usage" ); |
281 | static_assert( |
282 | SubsecondRatio::num == 1, "subsecond numerator should always be 1" ); |
283 | |
284 | if (LIKELY(seconds >= 0)) { |
285 | constexpr auto maxCount = std::numeric_limits<typename Tgt::rep>::max(); |
286 | constexpr auto maxSeconds = maxCount / Tgt::period::den; |
287 | |
288 | auto unsignedSeconds = to_unsigned(seconds); |
289 | if (LIKELY(unsignedSeconds < maxSeconds)) { |
290 | return ConversionCode::SUCCESS; |
291 | } |
292 | |
293 | if (UNLIKELY(unsignedSeconds == maxSeconds)) { |
294 | constexpr auto maxRemainder = |
295 | maxCount - (maxSeconds * Tgt::period::den); |
296 | constexpr auto maxSubseconds = |
297 | (maxRemainder * SubsecondRatio::den) / Tgt::period::den; |
298 | if (subseconds <= 0) { |
299 | return ConversionCode::SUCCESS; |
300 | } |
301 | if (to_unsigned(subseconds) <= maxSubseconds) { |
302 | return ConversionCode::SUCCESS; |
303 | } |
304 | } |
305 | return ConversionCode::POSITIVE_OVERFLOW; |
306 | } else if (std::is_unsigned<typename Tgt::rep>::value) { |
307 | return ConversionCode::NEGATIVE_OVERFLOW; |
308 | } else { |
309 | constexpr auto minCount = |
310 | to_signed(std::numeric_limits<typename Tgt::rep>::lowest()); |
311 | constexpr auto minSeconds = (minCount / Tgt::period::den); |
312 | if (LIKELY(seconds >= minSeconds)) { |
313 | return ConversionCode::SUCCESS; |
314 | } |
315 | |
316 | if (UNLIKELY(seconds == minSeconds - 1)) { |
317 | constexpr auto maxRemainder = |
318 | minCount - (minSeconds * Tgt::period::den) + Tgt::period::den; |
319 | constexpr auto maxSubseconds = |
320 | (maxRemainder * SubsecondRatio::den) / Tgt::period::den; |
321 | if (subseconds <= 0) { |
322 | return ConversionCode::NEGATIVE_OVERFLOW; |
323 | } |
324 | if (subseconds >= maxSubseconds) { |
325 | return ConversionCode::SUCCESS; |
326 | } |
327 | } |
328 | return ConversionCode::NEGATIVE_OVERFLOW; |
329 | } |
330 | } |
331 | }; |
332 | |
333 | template <> |
334 | struct CheckOverflowToDuration<true> { |
335 | template < |
336 | typename Tgt, |
337 | typename SubsecondRatio, |
338 | typename Seconds, |
339 | typename Subseconds> |
340 | static ConversionCode check( |
341 | Seconds /* seconds */, |
342 | Subseconds /* subseconds */) { |
343 | static_assert( |
344 | std::is_floating_point<typename Tgt::rep>::value, "incorrect usage" ); |
345 | static_assert( |
346 | SubsecondRatio::num == 1, "subsecond numerator should always be 1" ); |
347 | |
348 | // We expect floating point types to have much a wider representable range |
349 | // than integer types, so we don't bother actually checking the input |
350 | // integer value here. |
351 | static_assert( |
352 | std::numeric_limits<typename Tgt::rep>::max() >= |
353 | std::numeric_limits<Seconds>::max(), |
354 | "unusually limited floating point type" ); |
355 | static_assert( |
356 | std::numeric_limits<typename Tgt::rep>::lowest() <= |
357 | std::numeric_limits<Seconds>::lowest(), |
358 | "unusually limited floating point type" ); |
359 | |
360 | return ConversionCode::SUCCESS; |
361 | } |
362 | }; |
363 | |
364 | /** |
365 | * Convert a timeval or a timespec to a std::chrono::duration with second |
366 | * granularity. |
367 | * |
368 | * The SubsecondRatio template parameter specifies what type of subseconds to |
369 | * return. This must have a numerator of 1. |
370 | * |
371 | * The input must be in normalized form: the subseconds field must be greater |
372 | * than or equal to 0, and less than SubsecondRatio::den (i.e., less than 1 |
373 | * second). |
374 | */ |
375 | template < |
376 | typename SubsecondRatio, |
377 | typename Seconds, |
378 | typename Subseconds, |
379 | typename Rep> |
380 | auto posixTimeToDuration( |
381 | Seconds seconds, |
382 | Subseconds subseconds, |
383 | std::chrono::duration<Rep, std::ratio<1, 1>> dummy) |
384 | -> Expected<decltype(dummy), ConversionCode> { |
385 | using Tgt = decltype(dummy); |
386 | static_assert(Tgt::period::num == 1, "special case expecting num==1" ); |
387 | static_assert(Tgt::period::den == 1, "special case expecting den==1" ); |
388 | static_assert( |
389 | SubsecondRatio::num == 1, "subsecond numerator should always be 1" ); |
390 | |
391 | auto outputSeconds = tryTo<typename Tgt::rep>(seconds); |
392 | if (outputSeconds.hasError()) { |
393 | return makeUnexpected(outputSeconds.error()); |
394 | } |
395 | |
396 | if (std::is_floating_point<typename Tgt::rep>::value) { |
397 | return Tgt{typename Tgt::rep(seconds) + |
398 | (typename Tgt::rep(subseconds) / SubsecondRatio::den)}; |
399 | } |
400 | |
401 | // If the value is negative, we have to round up a non-zero subseconds value |
402 | if (UNLIKELY(outputSeconds.value() < 0) && subseconds > 0) { |
403 | if (UNLIKELY( |
404 | outputSeconds.value() == |
405 | std::numeric_limits<typename Tgt::rep>::lowest())) { |
406 | return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); |
407 | } |
408 | return Tgt{outputSeconds.value() + 1}; |
409 | } |
410 | |
411 | return Tgt{outputSeconds.value()}; |
412 | } |
413 | |
414 | /** |
415 | * Convert a timeval or a timespec to a std::chrono::duration with subsecond |
416 | * granularity |
417 | */ |
418 | template < |
419 | typename SubsecondRatio, |
420 | typename Seconds, |
421 | typename Subseconds, |
422 | typename Rep, |
423 | std::intmax_t Denominator> |
424 | auto posixTimeToDuration( |
425 | Seconds seconds, |
426 | Subseconds subseconds, |
427 | std::chrono::duration<Rep, std::ratio<1, Denominator>> dummy) |
428 | -> Expected<decltype(dummy), ConversionCode> { |
429 | using Tgt = decltype(dummy); |
430 | static_assert(Tgt::period::num == 1, "special case expecting num==1" ); |
431 | static_assert(Tgt::period::den != 1, "special case expecting den!=1" ); |
432 | static_assert( |
433 | SubsecondRatio::num == 1, "subsecond numerator should always be 1" ); |
434 | |
435 | auto errorCode = detail::CheckOverflowToDuration< |
436 | std::is_floating_point<typename Tgt::rep>::value>:: |
437 | template check<Tgt, SubsecondRatio>(seconds, subseconds); |
438 | if (errorCode != ConversionCode::SUCCESS) { |
439 | return makeUnexpected(errorCode); |
440 | } |
441 | |
442 | if (LIKELY(seconds >= 0)) { |
443 | return std::chrono::duration_cast<Tgt>( |
444 | std::chrono::duration<typename Tgt::rep>{seconds}) + |
445 | std::chrono::duration_cast<Tgt>( |
446 | std::chrono::duration<typename Tgt::rep, SubsecondRatio>{ |
447 | subseconds}); |
448 | } else { |
449 | // For negative numbers we have to round subseconds up towards zero, even |
450 | // though it is a positive value, since the overall value is negative. |
451 | return std::chrono::duration_cast<Tgt>( |
452 | std::chrono::duration<typename Tgt::rep>{seconds + 1}) - |
453 | std::chrono::duration_cast<Tgt>( |
454 | std::chrono::duration<typename Tgt::rep, SubsecondRatio>{ |
455 | SubsecondRatio::den - subseconds}); |
456 | } |
457 | } |
458 | |
459 | /** |
460 | * Convert a timeval or a timespec to a std::chrono::duration with |
461 | * granularity coarser than 1 second. |
462 | */ |
463 | template < |
464 | typename SubsecondRatio, |
465 | typename Seconds, |
466 | typename Subseconds, |
467 | typename Rep, |
468 | std::intmax_t Numerator> |
469 | auto posixTimeToDuration( |
470 | Seconds seconds, |
471 | Subseconds subseconds, |
472 | std::chrono::duration<Rep, std::ratio<Numerator, 1>> dummy) |
473 | -> Expected<decltype(dummy), ConversionCode> { |
474 | using Tgt = decltype(dummy); |
475 | static_assert(Tgt::period::num != 1, "special case expecting num!=1" ); |
476 | static_assert(Tgt::period::den == 1, "special case expecting den==1" ); |
477 | static_assert( |
478 | SubsecondRatio::num == 1, "subsecond numerator should always be 1" ); |
479 | |
480 | if (UNLIKELY(seconds < 0) && subseconds > 0) { |
481 | // Increment seconds by one to handle truncation of negative numbers |
482 | // properly. |
483 | if (UNLIKELY(seconds == std::numeric_limits<Seconds>::lowest())) { |
484 | return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); |
485 | } |
486 | seconds += 1; |
487 | } |
488 | |
489 | if (std::is_floating_point<typename Tgt::rep>::value) { |
490 | // Convert to the floating point type before performing the division |
491 | return Tgt{static_cast<typename Tgt::rep>(seconds) / Tgt::period::num}; |
492 | } else { |
493 | // Perform the division as an integer, and check that the result fits in |
494 | // the output integer type |
495 | auto outputValue = (seconds / Tgt::period::num); |
496 | auto expectedOuput = tryTo<typename Tgt::rep>(outputValue); |
497 | if (expectedOuput.hasError()) { |
498 | return makeUnexpected(expectedOuput.error()); |
499 | } |
500 | |
501 | return Tgt{expectedOuput.value()}; |
502 | } |
503 | } |
504 | |
505 | /** |
506 | * Convert a timeval or timespec to a std::chrono::duration |
507 | * |
508 | * This overload is only used for unusual durations where neither the numerator |
509 | * nor denominator are 1. |
510 | */ |
511 | template < |
512 | typename SubsecondRatio, |
513 | typename Seconds, |
514 | typename Subseconds, |
515 | typename Rep, |
516 | std::intmax_t Denominator, |
517 | std::intmax_t Numerator> |
518 | auto posixTimeToDuration( |
519 | Seconds seconds, |
520 | Subseconds subseconds, |
521 | std::chrono::duration<Rep, std::ratio<Numerator, Denominator>> dummy) |
522 | -> Expected<decltype(dummy), ConversionCode> { |
523 | using Tgt = decltype(dummy); |
524 | static_assert( |
525 | Tgt::period::num != 1, "should use special-case code when num==1" ); |
526 | static_assert( |
527 | Tgt::period::den != 1, "should use special-case code when den==1" ); |
528 | static_assert( |
529 | SubsecondRatio::num == 1, "subsecond numerator should always be 1" ); |
530 | |
531 | // Cast through an intermediate type with subsecond granularity. |
532 | // Note that this could fail due to overflow during the initial conversion |
533 | // even if the result is representable in the output POSIX-style types. |
534 | // |
535 | // Note that for integer type conversions going through this intermediate |
536 | // type can result in slight imprecision due to truncating the intermediate |
537 | // calculation to an integer. |
538 | using IntermediateType = |
539 | IntermediateDuration<typename Tgt::rep, typename Tgt::period>; |
540 | auto intermediate = posixTimeToDuration<SubsecondRatio>( |
541 | seconds, subseconds, IntermediateType{}); |
542 | if (intermediate.hasError()) { |
543 | return makeUnexpected(intermediate.error()); |
544 | } |
545 | // Now convert back to the target duration. Use tryTo() to confirm that the |
546 | // result fits in the target representation type. |
547 | return tryTo<typename Tgt::rep>( |
548 | intermediate.value().count() / Tgt::period::num) |
549 | .then([](typename Tgt::rep tgt) { return Tgt{tgt}; }); |
550 | } |
551 | |
552 | template < |
553 | typename Tgt, |
554 | typename SubsecondRatio, |
555 | typename Seconds, |
556 | typename Subseconds> |
557 | Expected<Tgt, ConversionCode> tryPosixTimeToDuration( |
558 | Seconds seconds, |
559 | Subseconds subseconds) { |
560 | static_assert( |
561 | SubsecondRatio::num == 1, "subsecond numerator should always be 1" ); |
562 | |
563 | // Normalize the input if required |
564 | if (UNLIKELY(subseconds < 0)) { |
565 | const auto overflowSeconds = (subseconds / SubsecondRatio::den); |
566 | const auto remainder = (subseconds % SubsecondRatio::den); |
567 | if (std::numeric_limits<Seconds>::lowest() + 1 - overflowSeconds > |
568 | seconds) { |
569 | return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); |
570 | } |
571 | seconds = seconds - 1 + overflowSeconds; |
572 | subseconds = remainder + SubsecondRatio::den; |
573 | } else if (UNLIKELY(subseconds >= SubsecondRatio::den)) { |
574 | const auto overflowSeconds = (subseconds / SubsecondRatio::den); |
575 | const auto remainder = (subseconds % SubsecondRatio::den); |
576 | if (std::numeric_limits<Seconds>::max() - overflowSeconds < seconds) { |
577 | return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); |
578 | } |
579 | seconds += overflowSeconds; |
580 | subseconds = remainder; |
581 | } |
582 | |
583 | return posixTimeToDuration<SubsecondRatio>(seconds, subseconds, Tgt{}); |
584 | } |
585 | |
586 | } // namespace detail |
587 | |
588 | /** |
589 | * struct timespec to std::chrono::duration |
590 | */ |
591 | template <typename Tgt> |
592 | typename std::enable_if< |
593 | detail::is_duration<Tgt>::value, |
594 | Expected<Tgt, ConversionCode>>::type |
595 | tryTo(const struct timespec& ts) { |
596 | return detail::tryPosixTimeToDuration<Tgt, std::nano>(ts.tv_sec, ts.tv_nsec); |
597 | } |
598 | |
599 | /** |
600 | * struct timeval to std::chrono::duration |
601 | */ |
602 | template <typename Tgt> |
603 | typename std::enable_if< |
604 | detail::is_duration<Tgt>::value, |
605 | Expected<Tgt, ConversionCode>>::type |
606 | tryTo(const struct timeval& tv) { |
607 | return detail::tryPosixTimeToDuration<Tgt, std::micro>(tv.tv_sec, tv.tv_usec); |
608 | } |
609 | |
610 | /** |
611 | * timespec or timeval to std::chrono::time_point |
612 | */ |
613 | template <typename Tgt, typename Src> |
614 | typename std::enable_if< |
615 | detail::is_time_point<Tgt>::value && detail::is_posix_time_type<Src>::value, |
616 | Expected<Tgt, ConversionCode>>::type |
617 | tryTo(const Src& value) { |
618 | return tryTo<typename Tgt::duration>(value).then( |
619 | [](typename Tgt::duration result) { return Tgt(result); }); |
620 | } |
621 | |
622 | /** |
623 | * std::chrono::duration to struct timespec |
624 | */ |
625 | template <typename Tgt, typename Rep, typename Period> |
626 | typename std::enable_if< |
627 | std::is_same<Tgt, struct timespec>::value, |
628 | Expected<Tgt, ConversionCode>>::type |
629 | tryTo(const std::chrono::duration<Rep, Period>& duration) { |
630 | auto result = detail::durationToPosixTime<std::nano>(duration); |
631 | if (result.hasError()) { |
632 | return makeUnexpected(result.error()); |
633 | } |
634 | |
635 | struct timespec ts; |
636 | ts.tv_sec = result.value().first; |
637 | ts.tv_nsec = result.value().second; |
638 | return ts; |
639 | } |
640 | |
641 | /** |
642 | * std::chrono::duration to struct timeval |
643 | */ |
644 | template <typename Tgt, typename Rep, typename Period> |
645 | typename std::enable_if< |
646 | std::is_same<Tgt, struct timeval>::value, |
647 | Expected<Tgt, ConversionCode>>::type |
648 | tryTo(const std::chrono::duration<Rep, Period>& duration) { |
649 | auto result = detail::durationToPosixTime<std::micro>(duration); |
650 | if (result.hasError()) { |
651 | return makeUnexpected(result.error()); |
652 | } |
653 | |
654 | struct timeval tv; |
655 | tv.tv_sec = result.value().first; |
656 | tv.tv_usec = result.value().second; |
657 | return tv; |
658 | } |
659 | |
660 | /** |
661 | * std::chrono::time_point to timespec or timeval |
662 | */ |
663 | template <typename Tgt, typename Clock, typename Duration> |
664 | typename std::enable_if< |
665 | detail::is_posix_time_type<Tgt>::value, |
666 | Expected<Tgt, ConversionCode>>::type |
667 | tryTo(const std::chrono::time_point<Clock, Duration>& timePoint) { |
668 | return tryTo<Tgt>(timePoint.time_since_epoch()); |
669 | } |
670 | |
671 | /** |
672 | * For all chrono conversions, to() wraps tryTo() |
673 | */ |
674 | template <typename Tgt, typename Src> |
675 | typename std::enable_if<detail::is_chrono_conversion<Tgt, Src>::value, Tgt>:: |
676 | type |
677 | to(const Src& value) { |
678 | return tryTo<Tgt>(value).thenOrThrow( |
679 | [](Tgt res) { return res; }, |
680 | [&](ConversionCode e) { return makeConversionError(e, StringPiece{}); }); |
681 | } |
682 | |
683 | } // namespace folly |
684 | |