time_delta_from_string.cc 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/time/time_delta_from_string.h"
  5. #include <limits>
  6. #include <utility>
  7. #include "base/strings/string_util.h"
  8. #include "base/time/time.h"
  9. namespace base {
  10. namespace {
  11. // Strips the |expected| prefix from the start of the given string, returning
  12. // |true| if the strip operation succeeded or false otherwise.
  13. //
  14. // Example:
  15. //
  16. // StringPiece input("abc");
  17. // EXPECT_TRUE(ConsumePrefix(input, "a"));
  18. // EXPECT_EQ(input, "bc");
  19. //
  20. // Adapted from absl::ConsumePrefix():
  21. // https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/strings/strip.h?l=45&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c
  22. bool ConsumePrefix(StringPiece& str, StringPiece expected) {
  23. if (!StartsWith(str, expected))
  24. return false;
  25. str.remove_prefix(expected.size());
  26. return true;
  27. }
  28. // Utility struct used by ConsumeDurationNumber() to parse decimal numbers.
  29. // A ParsedDecimal represents the number `int_part` + `frac_part`/`frac_scale`,
  30. // where:
  31. // (i) 0 <= `frac_part` < `frac_scale` (implies `frac_part`/`frac_scale` < 1)
  32. // (ii) `frac_scale` is 10^[number of digits after the decimal point]
  33. //
  34. // Example:
  35. // -42 => {.int_part = -42, .frac_part = 0, .frac_scale = 1}
  36. // 1.23 => {.int_part = 1, .frac_part = 23, .frac_scale = 100}
  37. struct ParsedDecimal {
  38. int64_t int_part = 0;
  39. int64_t frac_part = 0;
  40. int64_t frac_scale = 1;
  41. };
  42. // A helper for FromString() that tries to parse a leading number from the given
  43. // StringPiece. |number_string| is modified to start from the first unconsumed
  44. // char.
  45. //
  46. // Adapted from absl:
  47. // https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/time/duration.cc?l=807&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c
  48. constexpr absl::optional<ParsedDecimal> ConsumeDurationNumber(
  49. StringPiece& number_string) {
  50. ParsedDecimal res;
  51. StringPiece::const_iterator orig_start = number_string.begin();
  52. // Parse contiguous digits.
  53. for (; !number_string.empty(); number_string.remove_prefix(1)) {
  54. const int d = number_string.front() - '0';
  55. if (d < 0 || d >= 10)
  56. break;
  57. if (res.int_part > std::numeric_limits<int64_t>::max() / 10)
  58. return absl::nullopt;
  59. res.int_part *= 10;
  60. if (res.int_part > std::numeric_limits<int64_t>::max() - d)
  61. return absl::nullopt;
  62. res.int_part += d;
  63. }
  64. const bool int_part_empty = number_string.begin() == orig_start;
  65. if (number_string.empty() || number_string.front() != '.')
  66. return int_part_empty ? absl::nullopt : absl::make_optional(res);
  67. number_string.remove_prefix(1); // consume '.'
  68. // Parse contiguous digits.
  69. for (; !number_string.empty(); number_string.remove_prefix(1)) {
  70. const int d = number_string.front() - '0';
  71. if (d < 0 || d >= 10)
  72. break;
  73. DCHECK_LT(res.frac_part, res.frac_scale);
  74. if (res.frac_scale <= std::numeric_limits<int64_t>::max() / 10) {
  75. // |frac_part| will not overflow because it is always < |frac_scale|.
  76. res.frac_part *= 10;
  77. res.frac_part += d;
  78. res.frac_scale *= 10;
  79. }
  80. }
  81. return int_part_empty && res.frac_scale == 1 ? absl::nullopt
  82. : absl::make_optional(res);
  83. }
  84. // A helper for FromString() that tries to parse a leading unit designator
  85. // (e.g., ns, us, ms, s, m, h) from the given StringPiece. |unit_string| is
  86. // modified to start from the first unconsumed char.
  87. //
  88. // Adapted from absl:
  89. // https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/time/duration.cc?l=841&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c
  90. absl::optional<TimeDelta> ConsumeDurationUnit(StringPiece& unit_string) {
  91. for (const auto& str_delta : {
  92. std::make_pair("ns", Nanoseconds(1)),
  93. std::make_pair("us", Microseconds(1)),
  94. // Note: "ms" MUST be checked before "m" to ensure that milliseconds
  95. // are not parsed as minutes.
  96. std::make_pair("ms", Milliseconds(1)),
  97. std::make_pair("s", Seconds(1)),
  98. std::make_pair("m", Minutes(1)),
  99. std::make_pair("h", Hours(1)),
  100. }) {
  101. if (ConsumePrefix(unit_string, str_delta.first))
  102. return str_delta.second;
  103. }
  104. return absl::nullopt;
  105. }
  106. } // namespace
  107. absl::optional<TimeDelta> TimeDeltaFromString(StringPiece duration_string) {
  108. int sign = 1;
  109. if (ConsumePrefix(duration_string, "-"))
  110. sign = -1;
  111. else
  112. ConsumePrefix(duration_string, "+");
  113. if (duration_string.empty())
  114. return absl::nullopt;
  115. // Handle special-case values that don't require units.
  116. if (duration_string == "0")
  117. return TimeDelta();
  118. if (duration_string == "inf")
  119. return sign == 1 ? TimeDelta::Max() : TimeDelta::Min();
  120. TimeDelta delta;
  121. while (!duration_string.empty()) {
  122. absl::optional<ParsedDecimal> number_opt =
  123. ConsumeDurationNumber(duration_string);
  124. if (!number_opt.has_value())
  125. return absl::nullopt;
  126. absl::optional<TimeDelta> unit_opt = ConsumeDurationUnit(duration_string);
  127. if (!unit_opt.has_value())
  128. return absl::nullopt;
  129. ParsedDecimal number = number_opt.value();
  130. TimeDelta unit = unit_opt.value();
  131. if (number.int_part != 0)
  132. delta += sign * number.int_part * unit;
  133. if (number.frac_part != 0)
  134. delta +=
  135. (static_cast<double>(sign) * number.frac_part / number.frac_scale) *
  136. unit;
  137. }
  138. return delta;
  139. }
  140. } // namespace base