blob: 59bc28ae71d1f440dac34801e716c7558422870d [file] [log] [blame]
// 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(std::is_integral<From>::value
? ValueAsString(before)
: std::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 (std::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 (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.
StatusOr<double> FloatToDouble(float before) {
// Casting float to double should just work as double has more precision
// than float.
return static_cast<double>(before);
}
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 static_cast<float>(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 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();
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 DoubleToFloat(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,
bool ignore_unknown_enum_values) 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 != 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 != 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."));
}
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, 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
} // namespace google