// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include #include #include #include #include #include namespace google { namespace protobuf { namespace util { namespace converter { ; ; ; using util::error::Code; using util::Status; using util::StatusOr; namespace { inline Status InvalidArgument(StringPiece value_str) { return Status(util::error::INVALID_ARGUMENT, value_str); } template StatusOr ValidateNumberConversion(To after, From before) { if (after == before && MathUtil::Sign(before) == MathUtil::Sign(after)) { return after; } else { return InvalidArgument(std::is_integral::value ? ValueAsString(before) : std::is_same::value ? DoubleAsString(before) : FloatAsString(before)); } } // For general conversion between // int32, int64, uint32, uint64, double and float // except conversion between double and float. template StatusOr NumberConvertAndCheck(From before) { if (std::is_same::value) return before; To after = static_cast(before); return ValidateNumberConversion(after, before); } // For conversion to integer types (int32, int64, uint32, uint64) from floating // point types (double, float) only. template StatusOr FloatingPointToIntConvertAndCheck(From before) { if (std::is_same::value) return before; To after = static_cast(before); return ValidateNumberConversion(after, before); } // For conversion between double and float only. StatusOr FloatToDouble(float before) { // Casting float to double should just work as double has more precision // than float. return static_cast(before); } StatusOr DoubleToFloat(double before) { if (MathLimits::IsNaN(before)) { return std::numeric_limits::quiet_NaN(); } else if (!MathLimits::IsFinite(before)) { // Converting a double +inf/-inf to float should just work. return static_cast(before); } else if (before > std::numeric_limits::max() || before < -std::numeric_limits::max()) { // Double value outside of the range of float. return InvalidArgument(DoubleAsString(before)); } else { return static_cast(before); } } } // namespace StatusOr DataPiece::ToInt32() const { if (type_ == TYPE_STRING) return StringToNumber(safe_strto32); if (type_ == TYPE_DOUBLE) return FloatingPointToIntConvertAndCheck(double_); if (type_ == TYPE_FLOAT) return FloatingPointToIntConvertAndCheck(float_); return GenericConvert(); } StatusOr DataPiece::ToUint32() const { if (type_ == TYPE_STRING) return StringToNumber(safe_strtou32); if (type_ == TYPE_DOUBLE) return FloatingPointToIntConvertAndCheck(double_); if (type_ == TYPE_FLOAT) return FloatingPointToIntConvertAndCheck(float_); return GenericConvert(); } StatusOr DataPiece::ToInt64() const { if (type_ == TYPE_STRING) return StringToNumber(safe_strto64); if (type_ == TYPE_DOUBLE) return FloatingPointToIntConvertAndCheck(double_); if (type_ == TYPE_FLOAT) return FloatingPointToIntConvertAndCheck(float_); return GenericConvert(); } StatusOr DataPiece::ToUint64() const { if (type_ == TYPE_STRING) return StringToNumber(safe_strtou64); if (type_ == TYPE_DOUBLE) return FloatingPointToIntConvertAndCheck(double_); if (type_ == TYPE_FLOAT) return FloatingPointToIntConvertAndCheck(float_); return GenericConvert(); } StatusOr DataPiece::ToDouble() const { if (type_ == TYPE_FLOAT) { return FloatToDouble(float_); } if (type_ == TYPE_STRING) { if (str_ == "Infinity") return std::numeric_limits::infinity(); if (str_ == "-Infinity") return -std::numeric_limits::infinity(); if (str_ == "NaN") return std::numeric_limits::quiet_NaN(); StatusOr value = StringToNumber(safe_strtod); if (value.ok() && !MathLimits::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(); } StatusOr DataPiece::ToFloat() const { if (type_ == TYPE_DOUBLE) { return DoubleToFloat(double_); } if (type_ == TYPE_STRING) { if (str_ == "Infinity") return std::numeric_limits::infinity(); if (str_ == "-Infinity") return -std::numeric_limits::infinity(); if (str_ == "NaN") return std::numeric_limits::quiet_NaN(); // SafeStrToFloat() is used instead of safe_strtof() because the later // does not fail on inputs like SimpleDtoa(DBL_MAX). return StringToNumber(SafeStrToFloat); } return GenericConvert(); } StatusOr DataPiece::ToBool() const { switch (type_) { case TYPE_BOOL: return bool_; case TYPE_STRING: return StringToNumber(safe_strtob); default: return InvalidArgument( ValueAsStringOrDefault("Wrong type. Cannot convert to Bool.")); } } StatusOr DataPiece::ToString() const { switch (type_) { case TYPE_STRING: return string(str_); case TYPE_BYTES: { string base64; Base64Escape(str_, &base64); return base64; } default: return InvalidArgument( ValueAsStringOrDefault("Cannot convert to string.")); } } string DataPiece::ValueAsStringOrDefault( StringPiece default_string) const { switch (type_) { case TYPE_INT32: return SimpleItoa(i32_); case TYPE_INT64: return SimpleItoa(i64_); case TYPE_UINT32: return SimpleItoa(u32_); case TYPE_UINT64: return SimpleItoa(u64_); case TYPE_DOUBLE: return DoubleAsString(double_); case TYPE_FLOAT: return FloatAsString(float_); case TYPE_BOOL: return SimpleBtoa(bool_); case TYPE_STRING: return StrCat("\"", str_.ToString(), "\""); case TYPE_BYTES: { string base64; WebSafeBase64Escape(str_, &base64); return StrCat("\"", base64, "\""); } case TYPE_NULL: return "null"; default: return string(default_string); } } StatusOr DataPiece::ToBytes() const { if (type_ == TYPE_BYTES) return str_.ToString(); if (type_ == TYPE_STRING) { string decoded; if (!DecodeBase64(str_, &decoded)) { return InvalidArgument(ValueAsStringOrDefault("Invalid data in input.")); } return decoded; } else { return InvalidArgument(ValueAsStringOrDefault( "Wrong type. Only String or Bytes can be converted to Bytes.")); } } StatusOr DataPiece::ToEnum(const google::protobuf::Enum* enum_type, bool use_lower_camel_for_enums, bool ignore_unknown_enum_values, bool* is_unknown_enum_value) const { if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE; if (type_ == TYPE_STRING) { // First try the given value as a name. string enum_name = string(str_); const google::protobuf::EnumValue* value = FindEnumValueByNameOrNull(enum_type, enum_name); if (value != nullptr) return value->number(); // Check if int version of enum is sent as string. StatusOr 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 != 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 ignored. if (ignore_unknown_enum_values) { *is_unknown_enum_value = true; 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.")); } template StatusOr DataPiece::GenericConvert() const { switch (type_) { case TYPE_INT32: return NumberConvertAndCheck(i32_); case TYPE_INT64: return NumberConvertAndCheck(i64_); case TYPE_UINT32: return NumberConvertAndCheck(u32_); case TYPE_UINT64: return NumberConvertAndCheck(u64_); case TYPE_DOUBLE: return NumberConvertAndCheck(double_); case TYPE_FLOAT: return NumberConvertAndCheck(float_); default: // TYPE_ENUM, TYPE_STRING, TYPE_CORD, TYPE_BOOL return InvalidArgument(ValueAsStringOrDefault( "Wrong type. Bool, Enum, String and Cord not supported in " "GenericConvert.")); } } template StatusOr 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("\"", string(str_), "\"")); } 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(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 } // namespace google