// 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 namespace google { namespace protobuf { namespace util { namespace converter { using google::protobuf::EnumDescriptor; using google::protobuf::EnumValueDescriptor; ; ; 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); } // For general conversion between // int32, int64, uint32, uint64, double and float // except conversion between double and float. template StatusOr NumberConvertAndCheck(From before) { if (::google::protobuf::internal::is_same::value) return before; To after = static_cast(before); if (after == before && MathUtil::Sign(before) == MathUtil::Sign(after)) { return after; } else { return InvalidArgument(::google::protobuf::internal::is_integral::value ? ValueAsString(before) : ::google::protobuf::internal::is_same::value ? DoubleAsString(before) : FloatAsString(before)); } } // For conversion between double and float only. template StatusOr FloatingPointConvertAndCheck(From before) { if (isnan(before)) return std::numeric_limits::quiet_NaN(); To after = static_cast(before); if (MathUtil::AlmostEquals(after, before)) { return after; } else { return InvalidArgument(::google::protobuf::internal::is_same::value ? DoubleAsString(before) : FloatAsString(before)); } } } // namespace StatusOr DataPiece::ToInt32() const { if (type_ == TYPE_STRING) { return StringToNumber(safe_strto32); } return GenericConvert(); } StatusOr DataPiece::ToUint32() const { if (type_ == TYPE_STRING) { return StringToNumber(safe_strtou32); } return GenericConvert(); } StatusOr DataPiece::ToInt64() const { if (type_ == TYPE_STRING) { return StringToNumber(safe_strto64); } return GenericConvert(); } StatusOr DataPiece::ToUint64() const { if (type_ == TYPE_STRING) { return StringToNumber(safe_strtou64); } return GenericConvert(); } StatusOr DataPiece::ToDouble() const { if (type_ == TYPE_FLOAT) { return FloatingPointConvertAndCheck(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(); return StringToNumber(safe_strtod); } return GenericConvert(); } StatusOr DataPiece::ToFloat() const { if (type_ == TYPE_DOUBLE) { return FloatingPointConvertAndCheck(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 str_.ToString(); case TYPE_BYTES: { string base64; WebSafeBase64Escape(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 default_string.ToString(); } } StatusOr 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.")); } } 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) 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 = str_.ToString(); const google::protobuf::EnumValue* value = FindEnumValueByNameOrNull(enum_type, enum_name); if (value != NULL) return 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 value = ToInt32(); if (value.ok()) { if (const google::protobuf::EnumValue* enum_value = FindEnumValueByNumberOrNull(enum_type, value.ValueOrDie())) { return enum_value->number(); } } } 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 { To result; if (func(str_, &result)) return result; return InvalidArgument(StrCat("\"", str_.ToString(), "\"")); } } // namespace converter } // namespace util } // namespace protobuf } // namespace google