Line data Source code
1 : #include "parse_util.h" 2 : 3 : #include <algorithm> 4 : #include <cassert> 5 : #include <cctype> 6 : #include <charconv> 7 : #include <iterator> 8 : #include <sstream> 9 : #include <string> 10 : 11 : #include "error.h" 12 : 13 : namespace datadog { 14 : namespace tracing { 15 : namespace { 16 : 17 : template <typename Integer> 18 512 : Expected<Integer> parse_integer(StringView input, int base, StringView kind) { 19 : Integer value; 20 512 : const auto status = std::from_chars(input.begin(), input.end(), value, base); 21 512 : if (status.ec == std::errc::invalid_argument) { 22 31 : std::string message; 23 31 : message += "Is not a valid integer: \""; 24 31 : append(message, input); 25 31 : message += '\"'; 26 31 : return Error{Error::INVALID_INTEGER, std::move(message)}; 27 512 : } else if (status.ptr != input.end()) { 28 27 : std::string message; 29 27 : message += "Integer has trailing characters in: \""; 30 27 : append(message, input); 31 27 : message += '\"'; 32 27 : return Error{Error::INVALID_INTEGER, std::move(message)}; 33 481 : } else if (status.ec == std::errc::result_out_of_range) { 34 16 : std::string message; 35 16 : message += "Integer is not within the range of "; 36 16 : append(message, kind); 37 16 : message += ": "; 38 16 : append(message, input); 39 16 : return Error{Error::OUT_OF_RANGE_INTEGER, std::move(message)}; 40 16 : } 41 438 : return value; 42 : } 43 : 44 : } // namespace 45 : 46 212 : StringView strip(StringView input) { 47 450 : const auto not_whitespace = [](unsigned char ch) { 48 450 : return !std::isspace(ch); 49 : }; 50 : const char* const begin = 51 212 : std::find_if(input.begin(), input.end(), not_whitespace); 52 : const char* const end = 53 212 : std::find_if(input.rbegin(), std::make_reverse_iterator(begin), 54 : not_whitespace) 55 212 : .base(); 56 : 57 212 : assert(begin <= end); 58 : 59 212 : return StringView{begin, std::size_t(end - begin)}; 60 : } 61 : 62 423 : Expected<std::uint64_t> parse_uint64(StringView input, int base) { 63 423 : return parse_integer<std::uint64_t>(input, base, "64-bit unsigned"); 64 : } 65 : 66 89 : Expected<int> parse_int(StringView input, int base) { 67 89 : return parse_integer<int>(input, base, "int"); 68 : } 69 : 70 23 : Expected<double> parse_double(StringView input) { 71 : // This function uses a different technique from `parse_integer`, because 72 : // some compilers with _partial_ support for C++17 do not implement the 73 : // floating point portions of `std::from_chars`: 74 : // <https://en.cppreference.com/w/cpp/compiler_support/17#C.2B.2B17_library_features>. 75 : // As an alternative, we could use either of `std::stod` or `std::istream`. 76 : // I choose `std::istream`. 77 : double value; 78 23 : std::stringstream stream; 79 23 : stream << input; 80 23 : stream >> value; 81 : 82 23 : if (!stream) { 83 14 : std::string message; 84 : message += 85 : "Is not a valid number, or is out of the range of double precision " 86 14 : "floating point: \""; 87 14 : append(message, input); 88 14 : message += '\"'; 89 14 : return Error{Error::INVALID_DOUBLE, std::move(message)}; 90 23 : } else if (!stream.eof()) { 91 2 : std::string message; 92 2 : message += "Number has trailing characters in: \""; 93 2 : append(message, input); 94 2 : message += '\"'; 95 2 : return Error{Error::INVALID_DOUBLE, std::move(message)}; 96 2 : } 97 : 98 7 : return value; 99 23 : } 100 : 101 20147 : bool starts_with(StringView subject, StringView prefix) { 102 20147 : if (prefix.size() > subject.size()) { 103 13 : return false; 104 : } 105 : 106 20134 : return std::mismatch(subject.begin(), subject.end(), prefix.begin()).second == 107 20134 : prefix.end(); 108 : } 109 : 110 95 : void to_lower(std::string& text) { 111 95 : std::transform(text.begin(), text.end(), text.begin(), 112 304 : [](unsigned char ch) { return std::tolower(ch); }); 113 95 : } 114 : 115 : } // namespace tracing 116 : } // namespace datadog