| 861 | |
| 862 | template <typename Decimal> |
| 863 | Status DecimalFromString(const char* type_name, std::string_view s, Decimal* out, |
| 864 | int32_t* precision, int32_t* scale) { |
| 865 | if (s.empty()) { |
| 866 | return Status::Invalid("Empty string cannot be converted to ", type_name); |
| 867 | } |
| 868 | |
| 869 | DecimalComponents dec; |
| 870 | if (!ParseDecimalComponents(s.data(), s.size(), &dec)) { |
| 871 | return Status::Invalid("The string '", s, "' is not a valid ", type_name, " number"); |
| 872 | } |
| 873 | |
| 874 | // Count number of significant digits (without leading zeros) |
| 875 | size_t first_non_zero = dec.whole_digits.find_first_not_of('0'); |
| 876 | size_t significant_digits = dec.fractional_digits.size(); |
| 877 | if (first_non_zero != std::string::npos) { |
| 878 | significant_digits += dec.whole_digits.size() - first_non_zero; |
| 879 | } |
| 880 | int32_t parsed_precision = static_cast<int32_t>(significant_digits); |
| 881 | |
| 882 | int32_t parsed_scale = 0; |
| 883 | if (dec.has_exponent) { |
| 884 | // parsed_scale = -exponent + fractional_digits, computed with overflow |
| 885 | // detection: an exponent of INT32_MIN ("0E-2147483648") makes the negation, |
| 886 | // and a near-INT32_MIN exponent the addition, signed-overflow UB otherwise. |
| 887 | if (internal::SubtractWithOverflow(static_cast<int32_t>(dec.fractional_digits.size()), |
| 888 | dec.exponent, &parsed_scale)) { |
| 889 | return Status::Invalid("The string '", s, "' cannot be represented as ", type_name); |
| 890 | } |
| 891 | } else { |
| 892 | parsed_scale = static_cast<int32_t>(dec.fractional_digits.size()); |
| 893 | } |
| 894 | |
| 895 | if (out != nullptr) { |
| 896 | static_assert(Decimal::kBitWidth % 64 == 0, "decimal bit-width not a multiple of 64"); |
| 897 | std::array<uint64_t, Decimal::kBitWidth / 64> little_endian_array{}; |
| 898 | ShiftAndAdd(dec.whole_digits, little_endian_array.data(), little_endian_array.size()); |
| 899 | ShiftAndAdd(dec.fractional_digits, little_endian_array.data(), |
| 900 | little_endian_array.size()); |
| 901 | *out = Decimal(bit_util::little_endian::ToNative(little_endian_array)); |
| 902 | if (dec.sign == '-') { |
| 903 | out->Negate(); |
| 904 | } |
| 905 | } |
| 906 | |
| 907 | if (parsed_scale < 0) { |
| 908 | // Force the scale to zero, to avoid negative scales (due to compatibility issues |
| 909 | // with external systems such as databases) |
| 910 | if (-parsed_scale > Decimal::kMaxScale) { |
| 911 | return Status::Invalid("The string '", s, "' cannot be represented as ", type_name); |
| 912 | } |
| 913 | if (out != nullptr) { |
| 914 | *out *= Decimal::GetScaleMultiplier(-parsed_scale); |
| 915 | } |
| 916 | parsed_precision -= parsed_scale; |
| 917 | parsed_scale = 0; |
| 918 | } |
| 919 | |
| 920 | if (precision != nullptr) { |
no test coverage detected