text: // 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 <protobuf/util/internal/datapiece.h>
  
  #include <protobuf/struct.pb.h>
  #include <protobuf/type.pb.h>
  #include <protobuf/descriptor.h>
  #include <protobuf/util/internal/utility.h>
  #include <protobuf/stubs/strutil.h>
  #include <protobuf/stubs/mathlimits.h>
  #include <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,
                                  bool use_lower_camel_for_enums) 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();
  
      // 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();
  
      // 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 != NULL) return value->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 <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 = StringPiece(src).substr(
            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 = StringPiece(src).substr(
            0, src.ends_with("=") ? 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
  