This is an automated email from the ASF dual-hosted git repository.

bneradt pushed a commit to branch dev-1-2-5
in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git

commit d0bf7ef9da8ce4b10295df69b4d15a48274634a9
Author: Alan M. Carroll <[email protected]>
AuthorDate: Fri May 22 07:52:22 2020 -0500

    Add svtod: convert TextView to double.
---
 code/include/swoc/TextView.h        | 17 ++++++++
 code/include/swoc/swoc_ip.h         |  8 ++--
 code/src/TextView.cc                | 80 +++++++++++++++++++++++++++++++++++++
 unit_tests/test_IntrusiveHashMap.cc |  3 ++
 unit_tests/test_TextView.cc         | 22 ++++++++++
 5 files changed, 126 insertions(+), 4 deletions(-)

diff --git a/code/include/swoc/TextView.h b/code/include/swoc/TextView.h
index fec8962..740639a 100644
--- a/code/include/swoc/TextView.h
+++ b/code/include/swoc/TextView.h
@@ -822,11 +822,28 @@ svto_radix(swoc::TextView &src) {
   return zret;
 }
 
+/// Convenience overload.
+/// @see @svto_radix(swoc::TextView &src)
 template <int N>
 uintmax_t
 svto_radix(swoc::TextView &&src) {
   return svto_radix<N>(src);
 }
+
+/** Parse @a text as a floating point number.
+ *
+ * @param text The input text.
+ * @param parsed Parsed text [out]
+ * @return The floating point value, or 0.0 if invalid input.
+ *
+ * If @a parsed is not @a nullptr then the span of characters parsed is put 
there. This can be
+ * used to check if the parse was scuccesful - on a failed parse, it will be 
empty.
+ *
+ * @note This should be within 1 epsilon of correct, although it doesn't 
guarantee picking
+ * the closest epsilon. It's more than sufficient for use in configurations, 
but possibly
+ * not for high precision work.
+ */
+double svtod(swoc::TextView text, swoc::TextView * parsed = nullptr);
 // ----------------------------------------------------------
 // Inline implementations.
 // Note: Why, you may ask, do I use @c TextView::self_type for return type 
instead of the
diff --git a/code/include/swoc/swoc_ip.h b/code/include/swoc/swoc_ip.h
index 30f7195..4398dd6 100644
--- a/code/include/swoc/swoc_ip.h
+++ b/code/include/swoc/swoc_ip.h
@@ -69,10 +69,10 @@ union IPEndpoint {
 
   /** Break a string in to IP address relevant tokens.
    *
-   * @param [in] src Source tex.t
-   * @param [out] host The host / address.
-   * @param [out] port The port.
-   * @param [out] rest Any text past the end of the IP address.
+   * @param src Source text. [in]
+   * @param host The host / address. [out]
+   * @param port The port. [out]
+   * @param rest Any text past the end of the IP address. [out]
    * @return @c true if an IP address was found, @c false otherwise.
    *
    * Any of the out parameters can be @c nullptr in which case they are not 
updated.
diff --git a/code/src/TextView.cc b/code/src/TextView.cc
index 7f1e5e7..5ae6863 100644
--- a/code/src/TextView.cc
+++ b/code/src/TextView.cc
@@ -13,6 +13,8 @@
 #include <cctype>
 #include <sstream>
 
+using namespace swoc::literals;
+
 int
 memcmp(std::string_view const &lhs, std::string_view const &rhs)
 {
@@ -162,6 +164,84 @@ svtou(TextView src, TextView *out, int base) {
   return zret;
 }
 
+double svtod(swoc::TextView text, swoc::TextView *parsed) {
+  // @return 10^e
+  auto pow10 = [](int e) -> double  {
+    double zret = 1.0;
+    double scale = 10.0;
+    if (e < 0) { // flip the scale and make @a e positive.
+      e = -e;
+      scale = 0.1;
+    }
+
+    // Walk the bits in the exponent @a e and multiply the scale for set bits.
+    while (e) {
+      if (e & 1) {
+        zret *= scale;
+      }
+      scale *= scale;
+      e >>= 1;
+    }
+    return zret;
+  };
+
+  if (text.empty()) {
+    return 0;
+  }
+
+  auto org_text = text; // save this to update @a parsed.
+  // Check just once and dump to a local copy if needed.
+  TextView local_parsed;
+  if (! parsed) {
+    parsed = &local_parsed;
+  }
+
+  // Handle leading sign.
+  int sign = 1;
+  if (*text == '-') {
+    ++text;
+    sign = -1;
+  } else if (*text == '+') {
+    ++text;
+  }
+  // Parse the leading whole part as an integer.
+  intmax_t whole = svto_radix<10>(text);
+  parsed->assign(org_text.data(), text.data());
+
+  if (text.empty()) {
+    return whole;
+  }
+
+  double frac = 0.0;
+  if (*text == '.') { // fractional part.
+    ++text;
+    double scale = 0.1;
+    while (text && isdigit(*text)) {
+      frac += scale * (*text++ - '0');
+      scale /= 10.0;
+    }
+  }
+
+  double exp = 1.0;
+  if (text.starts_with_nocase("e")) {
+    int exp_sign = 1;
+    ++text;
+    if (text) {
+      if (*text == '+') {
+        ++text;
+      } else if (*text == '-') {
+        ++text;
+        exp_sign = -1;
+      }
+    }
+    auto exp_part = svto_radix<10>(text);
+    exp = pow10(exp_part * exp_sign);
+  }
+
+  parsed->assign(org_text.data(), text.data());
+  return sign * (whole + frac) * exp;
+}
+
 // Do the template instantiations.
 template std::ostream& TextView::stream_write(std::ostream&, const TextView&) 
const;
 
diff --git a/unit_tests/test_IntrusiveHashMap.cc 
b/unit_tests/test_IntrusiveHashMap.cc
index 6a820eb..a27e325 100644
--- a/unit_tests/test_IntrusiveHashMap.cc
+++ b/unit_tests/test_IntrusiveHashMap.cc
@@ -234,3 +234,6 @@ TEST_CASE("IntrusiveHashMapManyStrings", 
"[IntrusiveHashMap]")
   }
   REQUIRE(miss_p == false);
 };
+
+TEST_CASE("IntrusiveHashMap Utilities", "[IntrusiveHashMap]") {
+}
diff --git a/unit_tests/test_TextView.cc b/unit_tests/test_TextView.cc
index 9fd4106..435c166 100644
--- a/unit_tests/test_TextView.cc
+++ b/unit_tests/test_TextView.cc
@@ -377,6 +377,28 @@ TEST_CASE("TextView Conversions", "[libswoc][TextView]")
   x = n3;
   REQUIRE(25 == swoc::svto_radix<8>(x));
   REQUIRE(x.size() == 0);
+
+  // floating point is never exact, so "good enough" is all that is 
measureable. This checks the
+  // value is within one epsilon (minimum change possible) of the compiler 
generated value.
+  auto fcmp = [](double lhs, double rhs) {
+      double tolerance = std::max( { 1.0, std::fabs(lhs) , std::fabs(rhs) } ) 
* std::numeric_limits<double>::epsilon();
+      return std::fabs(lhs - rhs) <= tolerance ;
+  };
+
+  REQUIRE(1.0 == swoc::svtod("1.0"));
+  REQUIRE(2.0 == swoc::svtod("2.0"));
+  REQUIRE(true == fcmp(0.1, swoc::svtod("0.1")));
+  REQUIRE(true == fcmp(0.1, swoc::svtod(".1")));
+  REQUIRE(true == fcmp(0.02, swoc::svtod("0.02")));
+  REQUIRE(true == fcmp(2.718281828, swoc::svtod("2.718281828")));
+  REQUIRE(true == fcmp(-2.718281828, swoc::svtod("-2.718281828")));
+  REQUIRE(true == fcmp(2.718281828, swoc::svtod("+2.718281828")));
+  REQUIRE(true == fcmp(0.004, swoc::svtod("4e-3")));
+  REQUIRE(true == fcmp(4e-3, swoc::svtod("4e-3")));
+  REQUIRE(true == fcmp(500000, swoc::svtod("5e5")));
+  REQUIRE(true == fcmp(5e5, swoc::svtod("5e+5")));
+  REQUIRE(true == fcmp(678900, swoc::svtod("6.789E5")));
+  REQUIRE(true == fcmp(6.789e5, swoc::svtod("6.789E+5")));
 }
 
 TEST_CASE("TransformView", "[libswoc][TransformView]")

Reply via email to