about summary refs log tree commit diff
path: root/absl/time
diff options
context:
space:
mode:
authorAbseil Team <absl-team@google.com>2017-11-22T15·42-0800
committerAshley Hedberg <ahedberg@google.com>2017-11-22T20·25-0500
commitae0cef35ae5a7bfd873fb87babdd4900cbdb073c (patch)
tree425b9cea24813eb4f158a47a3f058d86edb8d1a8 /absl/time
parent6a88b407715315e99b372baa82a01f26608091dc (diff)
Changes imported from Abseil "staging" branch:
  - 55c7dd8ad1570b4e6ce2103ed4d4b6becdea0d96 Remove line continuations which require CMake >= 3.0.  Al... by Jon Cohen <cohenjon@google.com>
  - ee66ad72a90259d6286bbfea7241ed976bb0f6fb Change absl::ParseDuration() to avoid double.  This allow... by Abseil Team <absl-team@google.com>
  - 89cf4cd49d8ff25cb3d29f06b2090029a2024e89 Internal change by Gennadiy Rozental <rogeeff@google.com>
  - cdb5879bf6aaf6bbd2ad1fe4a2b144bbdf0389c7 Internal change by Gennadiy Rozental <rogeeff@google.com>
  - e7b29d11bf24a63bf7637689ada8be7d619844fc Internal change by Gennadiy Rozental <rogeeff@google.com>
  - 2d4fc08d5d64a7760ad6230eccdb5b8014c2b0c3 Update the exception-safety testing framework. by Jon Cohen <cohenjon@google.com>

GitOrigin-RevId: 55c7dd8ad1570b4e6ce2103ed4d4b6becdea0d96
Change-Id: I6b560cbc4570dfc5aa9a2f90e84d69904df7eac5
Diffstat (limited to 'absl/time')
-rw-r--r--absl/time/duration.cc46
-rw-r--r--absl/time/duration_test.cc13
2 files changed, 46 insertions, 13 deletions
diff --git a/absl/time/duration.cc b/absl/time/duration.cc
index f5081955d3bd..5e4f93136cdb 100644
--- a/absl/time/duration.cc
+++ b/absl/time/duration.cc
@@ -672,7 +672,7 @@ namespace {
 char* Format64(char* ep, int width, int64_t v) {
   do {
     --width;
-    *--ep = "0123456789"[v % 10];
+    *--ep = '0' + (v % 10);  // contiguous digits
   } while (v /= 10);
   while (--width >= 0) *--ep = '0';  // zero pad
   return ep;
@@ -782,15 +782,32 @@ std::string FormatDuration(Duration d) {
 namespace {
 
 // A helper for ParseDuration() that parses a leading number from the given
-// std::string and stores the result in *n.  The given std::string pointer is modified
-// to point to the first unconsumed char.
-bool ConsumeDurationNumber(const char** start, double* n) {
-  const char* s = *start;
-  char* end = nullptr;
-  errno = 0;
-  *n = strtod(s, &end);
-  *start = end;
-  return !std::isspace(*s) && errno == 0 && end != s && *n >= 0;
+// std::string and stores the result in *int_part/*frac_part/*frac_scale.  The
+// given std::string pointer is modified to point to the first unconsumed char.
+bool ConsumeDurationNumber(const char** dpp, int64_t* int_part,
+                           int64_t* frac_part, int64_t* frac_scale) {
+  *int_part = 0;
+  *frac_part = 0;
+  *frac_scale = 1;  // invariant: *frac_part < *frac_scale
+  const char* start = *dpp;
+  for (; std::isdigit(**dpp); *dpp += 1) {
+    const int d = **dpp - '0';  // contiguous digits
+    if (*int_part > kint64max / 10) return false;
+    *int_part *= 10;
+    if (*int_part > kint64max - d) return false;
+    *int_part += d;
+  }
+  const bool int_part_empty = (*dpp == start);
+  if (**dpp != '.') return !int_part_empty;
+  for (*dpp += 1; std::isdigit(**dpp); *dpp += 1) {
+    const int d = **dpp - '0';  // contiguous digits
+    if (*frac_scale <= kint64max / 10) {
+      *frac_part *= 10;
+      *frac_part += d;
+      *frac_scale *= 10;
+    }
+  }
+  return !int_part_empty || *frac_scale != 1;
 }
 
 // A helper for ParseDuration() that parses a leading unit designator (e.g.,
@@ -859,13 +876,16 @@ bool ParseDuration(const std::string& dur_string, Duration* d) {
 
   Duration dur;
   while (*start != '\0') {
-    double n = 0;
+    int64_t int_part;
+    int64_t frac_part;
+    int64_t frac_scale;
     Duration unit;
-    if (!ConsumeDurationNumber(&start, &n) ||
+    if (!ConsumeDurationNumber(&start, &int_part, &frac_part, &frac_scale) ||
         !ConsumeDurationUnit(&start, &unit)) {
       return false;
     }
-    dur += sign * n * unit;
+    if (int_part != 0) dur += sign * int_part * unit;
+    if (frac_part != 0) dur += sign * frac_part * unit / frac_scale;
   }
   *d = dur;
   return true;
diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc
index 7f4522874861..7918e1677052 100644
--- a/absl/time/duration_test.cc
+++ b/absl/time/duration_test.cc
@@ -1573,6 +1573,7 @@ TEST(Duration, ParseDuration) {
   EXPECT_FALSE(absl::ParseDuration("2s ", &d));
   EXPECT_FALSE(absl::ParseDuration(" 2s ", &d));
   EXPECT_FALSE(absl::ParseDuration("2mt", &d));
+  EXPECT_FALSE(absl::ParseDuration("1e3s", &d));
 
   // One unit type.
   EXPECT_TRUE(absl::ParseDuration("1ns", &d));
@@ -1588,6 +1589,12 @@ TEST(Duration, ParseDuration) {
   EXPECT_TRUE(absl::ParseDuration("2h", &d));
   EXPECT_EQ(absl::Hours(2), d);
 
+  // Huge counts of a unit.
+  EXPECT_TRUE(absl::ParseDuration("9223372036854775807us", &d));
+  EXPECT_EQ(absl::Microseconds(9223372036854775807), d);
+  EXPECT_TRUE(absl::ParseDuration("-9223372036854775807us", &d));
+  EXPECT_EQ(absl::Microseconds(-9223372036854775807), d);
+
   // Multiple units.
   EXPECT_TRUE(absl::ParseDuration("2h3m4s", &d));
   EXPECT_EQ(absl::Hours(2) + absl::Minutes(3) + absl::Seconds(4), d);
@@ -1619,6 +1626,12 @@ TEST(Duration, ParseDuration) {
   EXPECT_TRUE(absl::ParseDuration("1.5h", &d));
   EXPECT_EQ(1.5 * absl::Hours(1), d);
 
+  // Huge fractional counts of a unit.
+  EXPECT_TRUE(absl::ParseDuration("0.4294967295s", &d));
+  EXPECT_EQ(absl::Nanoseconds(429496729) + absl::Nanoseconds(1) / 2, d);
+  EXPECT_TRUE(absl::ParseDuration("0.429496729501234567890123456789s", &d));
+  EXPECT_EQ(absl::Nanoseconds(429496729) + absl::Nanoseconds(1) / 2, d);
+
   // Negative durations.
   EXPECT_TRUE(absl::ParseDuration("-1s", &d));
   EXPECT_EQ(absl::Seconds(-1), d);