| // 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 <google/protobuf/util/internal/datapiece.h> |
| |
| #include <google/protobuf/struct.pb.h> |
| #include <google/protobuf/type.pb.h> |
| #include <google/protobuf/descriptor.h> |
| #include <google/protobuf/util/internal/utility.h> |
| #include <google/protobuf/stubs/strutil.h> |
| #include <google/protobuf/stubs/mathlimits.h> |
| #include <google/protobuf/stubs/mathutil.h> |
| |
| 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); |
| } |
| |
| template <typename To, typename From> |
| StatusOr<To> ValidateNumberConversion(To after, From before) { |
| if (after == before && |
| MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) { |
| return after; |
| } else { |
| return InvalidArgument(::google::protobuf::internal::is_integral<From>::value |
| ? ValueAsString(before) |
| : ::google::protobuf::internal::is_same<From, double>::value |
| ? DoubleAsString(before) |
| : FloatAsString(before)); |
| } |
| } |
| |
| // For general conversion between |
| // int32, int64, uint32, uint64, double and float |
| // 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; |
| |
| To after = static_cast<To>(before); |
| return ValidateNumberConversion(after, before); |
| } |
| |
| // For conversion to integer types (int32, int64, uint32, uint64) from floating |
| // 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; |
| |
| 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(); |
| } |
| |
| To after = static_cast<To>(before); |
| if (MathUtil::AlmostEquals<To>(after, before)) { |
| return after; |
| } else { |
| return InvalidArgument(::google::protobuf::internal::is_same<From, double>::value |
| ? DoubleAsString(before) |
| : FloatAsString(before)); |
| } |
| } |
| |
| } // namespace |
| |
| StatusOr<int32> DataPiece::ToInt32() const { |
| if (type_ == TYPE_STRING) return StringToNumber<int32>(safe_strto32); |
| |
| if (type_ == TYPE_DOUBLE) |
| return FloatingPointToIntConvertAndCheck<int32, double>(double_); |
| |
| if (type_ == TYPE_FLOAT) |
| return FloatingPointToIntConvertAndCheck<int32, float>(float_); |
| |
| return GenericConvert<int32>(); |
| } |
| |
| StatusOr<uint32> DataPiece::ToUint32() const { |
| if (type_ == TYPE_STRING) return StringToNumber<uint32>(safe_strtou32); |
| |
| if (type_ == TYPE_DOUBLE) |
| return FloatingPointToIntConvertAndCheck<uint32, double>(double_); |
| |
| if (type_ == TYPE_FLOAT) |
| return FloatingPointToIntConvertAndCheck<uint32, float>(float_); |
| |
| return GenericConvert<uint32>(); |
| } |
| |
| StatusOr<int64> DataPiece::ToInt64() const { |
| if (type_ == TYPE_STRING) return StringToNumber<int64>(safe_strto64); |
| |
| if (type_ == TYPE_DOUBLE) |
| return FloatingPointToIntConvertAndCheck<int64, double>(double_); |
| |
| if (type_ == TYPE_FLOAT) |
| return FloatingPointToIntConvertAndCheck<int64, float>(float_); |
| |
| return GenericConvert<int64>(); |
| } |
| |
| StatusOr<uint64> DataPiece::ToUint64() const { |
| if (type_ == TYPE_STRING) return StringToNumber<uint64>(safe_strtou64); |
| |
| if (type_ == TYPE_DOUBLE) |
| return FloatingPointToIntConvertAndCheck<uint64, double>(double_); |
| |
| if (type_ == TYPE_FLOAT) |
| return FloatingPointToIntConvertAndCheck<uint64, float>(float_); |
| |
| return GenericConvert<uint64>(); |
| } |
| |
| StatusOr<double> DataPiece::ToDouble() const { |
| if (type_ == TYPE_FLOAT) { |
| return FloatingPointConvertAndCheck<double, float>(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); |
| } |
| return GenericConvert<double>(); |
| } |
| |
| StatusOr<float> DataPiece::ToFloat() const { |
| if (type_ == TYPE_DOUBLE) { |
| return FloatingPointConvertAndCheck<float, double>(double_); |
| } |
| if (type_ == TYPE_STRING) { |
| if (str_ == "Infinity") return std::numeric_limits<float>::infinity(); |
| if (str_ == "-Infinity") return -std::numeric_limits<float>::infinity(); |
| if (str_ == "NaN") return std::numeric_limits<float>::quiet_NaN(); |
| // SafeStrToFloat() is used instead of safe_strtof() because the later |
| // does not fail on inputs like SimpleDtoa(DBL_MAX). |
| return StringToNumber<float>(SafeStrToFloat); |
| } |
| return GenericConvert<float>(); |
| } |
| |
| StatusOr<bool> DataPiece::ToBool() const { |
| switch (type_) { |
| case TYPE_BOOL: |
| return bool_; |
| case TYPE_STRING: |
| return StringToNumber<bool>(safe_strtob); |
| default: |
| return InvalidArgument( |
| ValueAsStringOrDefault("Wrong type. Cannot convert to Bool.")); |
| } |
| } |
| |
| StatusOr<string> DataPiece::ToString() const { |
| switch (type_) { |
| case TYPE_STRING: |
| return str_.ToString(); |
| 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 default_string.ToString(); |
| } |
| } |
| |
| StatusOr<string> 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<int> 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<int32> 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 <typename To> |
| StatusOr<To> DataPiece::GenericConvert() const { |
| switch (type_) { |
| case TYPE_INT32: |
| return NumberConvertAndCheck<To, int32>(i32_); |
| case TYPE_INT64: |
| return NumberConvertAndCheck<To, int64>(i64_); |
| case TYPE_UINT32: |
| return NumberConvertAndCheck<To, uint32>(u32_); |
| case TYPE_UINT64: |
| return NumberConvertAndCheck<To, uint64>(u64_); |
| case TYPE_DOUBLE: |
| return NumberConvertAndCheck<To, double>(double_); |
| case TYPE_FLOAT: |
| return NumberConvertAndCheck<To, float>(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 <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(src, 0, src.ends_with("=") |
| ? 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(src, 0, src.ends_with("=") |
| ? src.find_last_not_of('=') + 1 |
| : src.length()); |
| return encoded == src_no_padding; |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } // namespace converter |
| } // namespace util |
| } // namespace protobuf |
| } // namespace google |