proxygen
Conv.h
Go to the documentation of this file.
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 
27 #pragma once
28 
29 #include <chrono>
30 #include <type_traits>
31 
32 #include <folly/Conv.h>
33 #include <folly/Expected.h>
36 
37 namespace folly {
38 namespace detail {
39 
40 template <typename T>
42 template <typename Rep, typename Period>
43 struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type {};
44 template <typename T>
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>
51  static constexpr bool value =
53 };
54 template <typename T>
58 };
59 template <typename Tgt, typename Src>
61  static constexpr bool value =
64 };
65 
74 template <typename Src>
76  if (value > std::numeric_limits<time_t>::max()) {
78  }
80  if (value < std::numeric_limits<time_t>::lowest()) {
82  }
83  }
84 
85  return static_cast<time_t>(value);
86 }
87 
95 template <typename SubsecondRatio, typename Rep>
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;
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()) {
114  }
115  secValue -= 1;
116  subsec += SubsecondRatio::den;
117  }
118  }
119  return std::pair<time_t, long>{secValue, subsec};
120 }
121 
126 template <typename SubsecondRatio, typename Rep, std::intmax_t Denominator>
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()) {
144  }
145  secTimeT -= 1;
146  subsec += SubsecondRatio::den;
147  }
148 
149  return std::pair<time_t, long>{secTimeT, subsec};
150 }
151 
156 template <typename SubsecondRatio, typename Rep, std::intmax_t Numerator>
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) {
167  }
168  if (duration.count() < minValue) {
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;
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()) {
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>
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,
220  std::ratio<1, Period::den>>;
221 
228 template <typename SubsecondRatio, typename Rep, typename Period>
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 =
245  if (duration.count() > maxInput) {
247  }
248  constexpr auto minInput =
250  if (duration.count() < minInput) {
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 
267 template <bool IsFloatingPoint>
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(
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)) {
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) {
300  }
301  if (to_unsigned(subseconds) <= maxSubseconds) {
303  }
304  }
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)) {
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) {
323  }
324  if (subseconds >= maxSubseconds) {
326  }
327  }
329  }
330  }
331 };
332 
333 template <>
335  template <
336  typename Tgt,
337  typename SubsecondRatio,
338  typename Seconds,
339  typename Subseconds>
341  Seconds /* seconds */,
342  Subseconds /* subseconds */) {
343  static_assert(
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(
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 
361  }
362 };
363 
375 template <
376  typename SubsecondRatio,
377  typename Seconds,
378  typename Subseconds,
379  typename Rep>
381  Seconds seconds,
382  Subseconds subseconds,
383  std::chrono::duration<Rep, std::ratio<1, 1>> dummy)
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 
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())) {
407  }
408  return Tgt{outputSeconds.value() + 1};
409  }
410 
411  return Tgt{outputSeconds.value()};
412 }
413 
418 template <
419  typename SubsecondRatio,
420  typename Seconds,
421  typename Subseconds,
422  typename Rep,
423  std::intmax_t Denominator>
425  Seconds seconds,
426  Subseconds subseconds,
427  std::chrono::duration<Rep, std::ratio<1, Denominator>> dummy)
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<
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 
463 template <
464  typename SubsecondRatio,
465  typename Seconds,
466  typename Subseconds,
467  typename Rep,
468  std::intmax_t Numerator>
470  Seconds seconds,
471  Subseconds subseconds,
472  std::chrono::duration<Rep, std::ratio<Numerator, 1>> dummy)
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())) {
485  }
486  seconds += 1;
487  }
488 
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 
511 template <
512  typename SubsecondRatio,
513  typename Seconds,
514  typename Subseconds,
515  typename Rep,
516  std::intmax_t Denominator,
517  std::intmax_t Numerator>
519  Seconds seconds,
520  Subseconds subseconds,
521  std::chrono::duration<Rep, std::ratio<Numerator, Denominator>> dummy)
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 =
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>
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) {
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) {
578  }
579  seconds += overflowSeconds;
580  subseconds = remainder;
581  }
582 
583  return posixTimeToDuration<SubsecondRatio>(seconds, subseconds, Tgt{});
584 }
585 
586 } // namespace detail
587 
591 template <typename Tgt>
592 typename std::enable_if<
595 tryTo(const struct timespec& ts) {
596  return detail::tryPosixTimeToDuration<Tgt, std::nano>(ts.tv_sec, ts.tv_nsec);
597 }
598 
602 template <typename Tgt>
603 typename std::enable_if<
604  detail::is_duration<Tgt>::value,
606 tryTo(const struct timeval& tv) {
607  return detail::tryPosixTimeToDuration<Tgt, std::micro>(tv.tv_sec, tv.tv_usec);
608 }
609 
613 template <typename Tgt, typename Src>
614 typename std::enable_if<
617 tryTo(const Src& value) {
618  return tryTo<typename Tgt::duration>(value).then(
619  [](typename Tgt::duration result) { return Tgt(result); });
620 }
621 
625 template <typename Tgt, typename Rep, typename Period>
626 typename std::enable_if<
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 
644 template <typename Tgt, typename Rep, typename Period>
645 typename std::enable_if<
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 
663 template <typename Tgt, typename Clock, typename Duration>
664 typename std::enable_if<
667 tryTo(const std::chrono::time_point<Clock, Duration>& timePoint) {
668  return tryTo<Tgt>(timePoint.time_since_epoch());
669 }
670 
674 template <typename Tgt, typename Src>
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
static ConversionCode check(Seconds seconds, Subseconds subseconds)
Definition: Conv.h:274
LogLevel max
Definition: LogLevel.cpp:31
PskType type
static ConversionCode check(Seconds, Subseconds)
Definition: Conv.h:340
#define LIKELY(x)
Definition: Likely.h:47
auto posixTimeToDuration(Seconds seconds, Subseconds subseconds, std::chrono::duration< Rep, std::ratio< 1, 1 >> dummy) -> Expected< decltype(dummy), ConversionCode >
Definition: Conv.h:380
STL namespace.
folly::std T
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
Expected< Tgt, ConversionCode > tryPosixTimeToDuration(Seconds seconds, Subseconds subseconds)
Definition: Conv.h:557
ConversionCode
Definition: Conv.h:53
bool_constant< true > true_type
Definition: gtest-port.h:2210
constexpr auto to_unsigned(T const &t) -> typename std::make_unsigned< T >::type
Definition: Utility.h:399
static Expected< std::pair< time_t, long >, ConversionCode > durationToPosixTime(const std::chrono::duration< Rep, std::ratio< 1, 1 >> &duration)
Definition: Conv.h:96
LogLevel min
Definition: LogLevel.cpp:30
std::enable_if< detail::is_chrono_conversion< Tgt, Src >::value, Tgt >::type to(const Src &value)
Definition: Conv.h:677
constexpr Unexpected< typename std::decay< Error >::type > makeUnexpected(Error &&)
Definition: Expected.h:785
void dummy()
static const char *const value
Definition: Conv.cpp:50
constexpr auto to_signed(T const &t) -> typename std::make_signed< T >::type
Definition: Utility.h:389
Expected< time_t, ConversionCode > chronoRangeCheck(Src value)
Definition: Conv.h:75
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
bool_constant< false > false_type
Definition: gtest-port.h:2209
std::enable_if< detail::is_duration< Tgt >::value, Expected< Tgt, ConversionCode > >::type tryTo(const struct timespec &ts)
Definition: Conv.h:595
std::chrono::duration< typename IntermediateTimeRep< Rep, std::is_floating_point< Rep >::value, std::is_signed< Rep >::value >::type, std::ratio< 1, Period::den >> IntermediateDuration
Definition: Conv.h:220
#define UNLIKELY(x)
Definition: Likely.h:48
ConversionError makeConversionError(ConversionCode code, StringPiece input)
Definition: Conv.cpp:765