| 929 | |
| 930 | template <typename DecimalClass> |
| 931 | Status SimpleDecimalFromString(const char* type_name, std::string_view s, |
| 932 | DecimalClass* out, int32_t* precision, int32_t* scale) { |
| 933 | if (s.empty()) { |
| 934 | return Status::Invalid("Empty string cannot be converted to ", type_name); |
| 935 | } |
| 936 | |
| 937 | DecimalComponents dec; |
| 938 | if (!ParseDecimalComponents(s.data(), s.size(), &dec)) { |
| 939 | return Status::Invalid("The string '", s, "' is not a valid ", type_name, " number"); |
| 940 | } |
| 941 | |
| 942 | // count number of significant digits (without leading zeros) |
| 943 | size_t first_non_zero = dec.whole_digits.find_first_not_of('0'); |
| 944 | size_t significant_digits = dec.fractional_digits.size(); |
| 945 | if (first_non_zero != std::string::npos) { |
| 946 | significant_digits += dec.whole_digits.size() - first_non_zero; |
| 947 | } |
| 948 | int32_t parsed_precision = static_cast<int32_t>(significant_digits); |
| 949 | |
| 950 | int32_t parsed_scale = 0; |
| 951 | if (dec.has_exponent) { |
| 952 | // parsed_scale = -exponent + fractional_digits, computed with overflow |
| 953 | // detection: an exponent of INT32_MIN ("0E-2147483648") makes the negation, |
| 954 | // and a near-INT32_MIN exponent the addition, signed-overflow UB otherwise. |
| 955 | if (internal::SubtractWithOverflow(static_cast<int32_t>(dec.fractional_digits.size()), |
| 956 | dec.exponent, &parsed_scale)) { |
| 957 | return Status::Invalid("The string '", s, "' cannot be represented as ", type_name); |
| 958 | } |
| 959 | } else { |
| 960 | parsed_scale = static_cast<int32_t>(dec.fractional_digits.size()); |
| 961 | } |
| 962 | |
| 963 | if (out != nullptr) { |
| 964 | uint64_t value{0}; |
| 965 | ShiftAndAdd(dec.whole_digits, &value, 1); |
| 966 | ShiftAndAdd(dec.fractional_digits, &value, 1); |
| 967 | if (value > static_cast<uint64_t>( |
| 968 | std::numeric_limits<typename DecimalClass::ValueType>::max())) { |
| 969 | return Status::Invalid("The string '", s, "' cannot be represented as ", type_name); |
| 970 | } |
| 971 | |
| 972 | *out = DecimalClass(value); |
| 973 | if (dec.sign == '-') { |
| 974 | out->Negate(); |
| 975 | } |
| 976 | } |
| 977 | |
| 978 | if (parsed_scale < 0) { |
| 979 | // Force the scale to zero, to avoid negative scales (due to compatibility issues |
| 980 | // with external systems such as databases) |
| 981 | if (-parsed_scale > DecimalClass::kMaxScale) { |
| 982 | return Status::Invalid("The string '", s, "' cannot be represented as ", type_name); |
| 983 | } |
| 984 | if (out != nullptr) { |
| 985 | *out *= DecimalClass::GetScaleMultiplier(-parsed_scale); |
| 986 | } |
| 987 | parsed_precision -= parsed_scale; |
| 988 | parsed_scale = 0; |
no test coverage detected