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]")
