aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/google/protobuf/util/internal/datapiece.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/google/protobuf/util/internal/datapiece.cc')
-rw-r--r--src/google/protobuf/util/internal/datapiece.cc155
1 files changed, 122 insertions, 33 deletions
diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc
index b557429f..59bc28ae 100644
--- a/src/google/protobuf/util/internal/datapiece.cc
+++ b/src/google/protobuf/util/internal/datapiece.cc
@@ -47,6 +47,7 @@ using google::protobuf::EnumDescriptor;
using google::protobuf::EnumValueDescriptor;
;
;
+;
using util::error::Code;
using util::Status;
using util::StatusOr;
@@ -63,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));
}
@@ -76,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);
@@ -86,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);
}
}
@@ -161,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();
@@ -248,11 +261,8 @@ StatusOr<string> DataPiece::ToBytes() const {
if (type_ == TYPE_BYTES) return str_.ToString();
if (type_ == TYPE_STRING) {
string decoded;
- if (!WebSafeBase64Unescape(str_, &decoded)) {
- if (!Base64Unescape(str_, &decoded)) {
- return InvalidArgument(
- ValueAsStringOrDefault("Invalid data in input."));
- }
+ if (!DecodeBase64(str_, &decoded)) {
+ return InvalidArgument(ValueAsStringOrDefault("Invalid data in input."));
}
return decoded;
} else {
@@ -261,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) {
@@ -269,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."));
@@ -313,11 +343,70 @@ StatusOr<To> DataPiece::GenericConvert() const {
template <typename To>
StatusOr<To> DataPiece::StringToNumber(bool (*func)(StringPiece, To*)) const {
+ if (str_.size() > 0 && (str_[0] == ' ' || str_[str_.size() - 1] == ' ')) {
+ return InvalidArgument(StrCat("\"", str_, "\""));
+ }
To result;
if (func(str_, &result)) return result;
return InvalidArgument(StrCat("\"", str_.ToString(), "\""));
}
+bool DataPiece::DecodeBase64(StringPiece src, string* dest) const {
+ // Try web-safe decode first, if it fails, try the non-web-safe decode.
+ if (WebSafeBase64Unescape(src, dest)) {
+ if (use_strict_base64_decoding_) {
+ // In strict mode, check if the escaped version gives us the same value as
+ // unescaped.
+ string encoded;
+ // WebSafeBase64Escape does no padding by default.
+ WebSafeBase64Escape(*dest, &encoded);
+ // Remove trailing padding '=' characters before comparison.
+ StringPiece src_no_padding = StringPiece(src).substr(
+ 0, StringEndsWith(src, "=") ? src.find_last_not_of('=') + 1
+ : src.length());
+ return encoded == src_no_padding;
+ }
+ return true;
+ }
+
+ if (Base64Unescape(src, dest)) {
+ if (use_strict_base64_decoding_) {
+ string encoded;
+ Base64Escape(
+ reinterpret_cast<const unsigned char*>(dest->data()), dest->length(),
+ &encoded, false);
+ StringPiece src_no_padding = StringPiece(src).substr(
+ 0, StringEndsWith(src, "=") ? src.find_last_not_of('=') + 1
+ : src.length());
+ return encoded == src_no_padding;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+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:
+ case TYPE_UINT32:
+ case TYPE_UINT64:
+ case TYPE_DOUBLE:
+ case TYPE_FLOAT:
+ case TYPE_BOOL:
+ case TYPE_ENUM:
+ case TYPE_NULL:
+ case TYPE_BYTES:
+ case TYPE_STRING: {
+ str_ = other.str_;
+ break;
+ }
+ }
+}
+
} // namespace converter
} // namespace util
} // namespace protobuf