diff options
Diffstat (limited to 'src/google/protobuf/util/internal/datapiece.cc')
-rw-r--r-- | src/google/protobuf/util/internal/datapiece.cc | 95 |
1 files changed, 65 insertions, 30 deletions
diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc index ef8da427..59bc28ae 100644 --- a/src/google/protobuf/util/internal/datapiece.cc +++ b/src/google/protobuf/util/internal/datapiece.cc @@ -64,9 +64,9 @@ StatusOr<To> ValidateNumberConversion(To after, From before) { MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) { return after; } else { - return InvalidArgument(::google::protobuf::internal::is_integral<From>::value + return InvalidArgument(std::is_integral<From>::value ? ValueAsString(before) - : ::google::protobuf::internal::is_same<From, double>::value + : std::is_same<From, double>::value ? DoubleAsString(before) : FloatAsString(before)); } @@ -77,7 +77,7 @@ StatusOr<To> ValidateNumberConversion(To after, From before) { // except conversion between double and float. template <typename To, typename From> StatusOr<To> NumberConvertAndCheck(From before) { - if (::google::protobuf::internal::is_same<From, To>::value) return before; + if (std::is_same<From, To>::value) return before; To after = static_cast<To>(before); return ValidateNumberConversion(after, before); @@ -87,26 +87,31 @@ StatusOr<To> NumberConvertAndCheck(From before) { // point types (double, float) only. template <typename To, typename From> StatusOr<To> FloatingPointToIntConvertAndCheck(From before) { - if (::google::protobuf::internal::is_same<From, To>::value) return before; + if (std::is_same<From, To>::value) return before; To after = static_cast<To>(before); return ValidateNumberConversion(after, before); } // For conversion between double and float only. -template <typename To, typename From> -StatusOr<To> FloatingPointConvertAndCheck(From before) { - if (MathLimits<From>::IsNaN(before)) { - return std::numeric_limits<To>::quiet_NaN(); - } +StatusOr<double> FloatToDouble(float before) { + // Casting float to double should just work as double has more precision + // than float. + return static_cast<double>(before); +} - To after = static_cast<To>(before); - if (MathUtil::AlmostEquals<To>(after, before)) { - return after; +StatusOr<float> DoubleToFloat(double before) { + if (MathLimits<double>::IsNaN(before)) { + return std::numeric_limits<float>::quiet_NaN(); + } else if (!MathLimits<double>::IsFinite(before)) { + // Converting a double +inf/-inf to float should just work. + return static_cast<float>(before); + } else if (before > std::numeric_limits<float>::max() || + before < -std::numeric_limits<float>::max()) { + // Double value outside of the range of float. + return InvalidArgument(DoubleAsString(before)); } else { - return InvalidArgument(::google::protobuf::internal::is_same<From, double>::value - ? DoubleAsString(before) - : FloatAsString(before)); + return static_cast<float>(before); } } @@ -162,20 +167,27 @@ StatusOr<uint64> DataPiece::ToUint64() const { StatusOr<double> DataPiece::ToDouble() const { if (type_ == TYPE_FLOAT) { - return FloatingPointConvertAndCheck<double, float>(float_); + return FloatToDouble(float_); } if (type_ == TYPE_STRING) { if (str_ == "Infinity") return std::numeric_limits<double>::infinity(); if (str_ == "-Infinity") return -std::numeric_limits<double>::infinity(); if (str_ == "NaN") return std::numeric_limits<double>::quiet_NaN(); - return StringToNumber<double>(safe_strtod); + StatusOr<double> value = StringToNumber<double>(safe_strtod); + if (value.ok() && !MathLimits<double>::IsFinite(value.ValueOrDie())) { + // safe_strtod converts out-of-range values to +inf/-inf, but we want + // to report them as errors. + return InvalidArgument(StrCat("\"", str_, "\"")); + } else { + return value; + } } return GenericConvert<double>(); } StatusOr<float> DataPiece::ToFloat() const { if (type_ == TYPE_DOUBLE) { - return FloatingPointConvertAndCheck<float, double>(double_); + return DoubleToFloat(double_); } if (type_ == TYPE_STRING) { if (str_ == "Infinity") return std::numeric_limits<float>::infinity(); @@ -259,7 +271,9 @@ StatusOr<string> DataPiece::ToBytes() const { } } -StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type) const { +StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type, + bool use_lower_camel_for_enums, + bool ignore_unknown_enum_values) const { if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE; if (type_ == TYPE_STRING) { @@ -267,21 +281,39 @@ StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type) const { string enum_name = str_.ToString(); const google::protobuf::EnumValue* value = FindEnumValueByNameOrNull(enum_type, enum_name); - if (value != NULL) return value->number(); + if (value != nullptr) return value->number(); + + // Check if int version of enum is sent as string. + StatusOr<int32> int_value = ToInt32(); + if (int_value.ok()) { + if (const google::protobuf::EnumValue* enum_value = + FindEnumValueByNumberOrNull(enum_type, int_value.ValueOrDie())) { + return enum_value->number(); + } + } + // Next try a normalized name. for (string::iterator it = enum_name.begin(); it != enum_name.end(); ++it) { *it = *it == '-' ? '_' : ascii_toupper(*it); } value = FindEnumValueByNameOrNull(enum_type, enum_name); - if (value != NULL) return value->number(); - } else { - StatusOr<int32> value = ToInt32(); - if (value.ok()) { - if (const google::protobuf::EnumValue* enum_value = - FindEnumValueByNumberOrNull(enum_type, value.ValueOrDie())) { - return enum_value->number(); - } + if (value != nullptr) return value->number(); + + // If use_lower_camel_for_enums is true try with enum name without + // underscore. This will also accept camel case names as the enum_name has + // been normalized before. + if (use_lower_camel_for_enums) { + value = FindEnumValueByNameWithoutUnderscoreOrNull(enum_type, enum_name); + if (value != nullptr) return value->number(); } + + // If ignore_unknown_enum_values is true an unknown enum value is treated + // as the default + if (ignore_unknown_enum_values) return enum_type->enumvalue(0).number(); + } else { + // We don't need to check whether the value is actually declared in the + // enum because we preserve unknown enum values as well. + return ToInt32(); } return InvalidArgument( ValueAsStringOrDefault("Cannot find enum with given value.")); @@ -330,7 +362,8 @@ bool DataPiece::DecodeBase64(StringPiece src, string* dest) const { WebSafeBase64Escape(*dest, &encoded); // Remove trailing padding '=' characters before comparison. StringPiece src_no_padding = StringPiece(src).substr( - 0, src.ends_with("=") ? src.find_last_not_of('=') + 1 : src.length()); + 0, StringEndsWith(src, "=") ? src.find_last_not_of('=') + 1 + : src.length()); return encoded == src_no_padding; } return true; @@ -343,7 +376,8 @@ bool DataPiece::DecodeBase64(StringPiece src, string* dest) const { reinterpret_cast<const unsigned char*>(dest->data()), dest->length(), &encoded, false); StringPiece src_no_padding = StringPiece(src).substr( - 0, src.ends_with("=") ? src.find_last_not_of('=') + 1 : src.length()); + 0, StringEndsWith(src, "=") ? src.find_last_not_of('=') + 1 + : src.length()); return encoded == src_no_padding; } return true; @@ -354,6 +388,7 @@ bool DataPiece::DecodeBase64(StringPiece src, string* dest) const { void DataPiece::InternalCopy(const DataPiece& other) { type_ = other.type_; + use_strict_base64_decoding_ = other.use_strict_base64_decoding_; switch (type_) { case TYPE_INT32: case TYPE_INT64: |