diff options
Diffstat (limited to 'toolkit/components/protobuf/src/google/protobuf/util/internal/datapiece.cc')
-rw-r--r-- | toolkit/components/protobuf/src/google/protobuf/util/internal/datapiece.cc | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/toolkit/components/protobuf/src/google/protobuf/util/internal/datapiece.cc b/toolkit/components/protobuf/src/google/protobuf/util/internal/datapiece.cc new file mode 100644 index 0000000000..3e7aa84da7 --- /dev/null +++ b/toolkit/components/protobuf/src/google/protobuf/util/internal/datapiece.cc @@ -0,0 +1,441 @@ +// 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 <cmath> +#include <cstdint> +#include <limits> + +#include <google/protobuf/struct.pb.h> +#include <google/protobuf/type.pb.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/stubs/status.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/util/internal/utility.h> +#include <google/protobuf/stubs/mathutil.h> + +namespace google { +namespace protobuf { +namespace util { +namespace converter { + +using util::Status; + +namespace { + +template <typename To, typename From> +util::StatusOr<To> ValidateNumberConversion(To after, From before) { + if (after == before && + MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) { + return after; + } else { + return util::InvalidArgumentError( + 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> +util::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> +util::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. +util::StatusOr<double> FloatToDouble(float before) { + // Casting float to double should just work as double has more precision + // than float. + return static_cast<double>(before); +} + +util::StatusOr<float> DoubleToFloat(double before) { + if (std::isnan(before)) { + return std::numeric_limits<float>::quiet_NaN(); + } else if (!std::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()) { + // Some doubles are larger than the largest float, but after + // rounding they will be equal to the largest float. + // We can't just attempt the conversion because that has UB if + // the value really is out-of-range. + // Here we take advantage that 1/2-ing a large floating point + // will not lose precision. + double half_before = before * 0.5; + if (half_before < std::numeric_limits<float>::max() && + half_before > -std::numeric_limits<float>::max()) { + const float half_fmax = std::numeric_limits<float>::max() * 0.5f; + // If after being cut in half, the value is less than the largest float, + // then it's safe to convert it to float. Importantly, this conversion + // rounds in the same way that the original does. + float half_after = static_cast<float>(half_before); + if (half_after <= half_fmax && half_after >= -half_fmax) { + return half_after + half_after; + } + } + // Double value outside of the range of float. + return util::InvalidArgumentError(DoubleAsString(before)); + } else { + return static_cast<float>(before); + } +} + +} // namespace + +util::StatusOr<int32_t> DataPiece::ToInt32() const { + if (type_ == TYPE_STRING) + return StringToNumber<int32_t>(safe_strto32); + + if (type_ == TYPE_DOUBLE) + return FloatingPointToIntConvertAndCheck<int32_t, double>(double_); + + if (type_ == TYPE_FLOAT) + return FloatingPointToIntConvertAndCheck<int32_t, float>(float_); + + return GenericConvert<int32_t>(); +} + +util::StatusOr<uint32_t> DataPiece::ToUint32() const { + if (type_ == TYPE_STRING) + return StringToNumber<uint32_t>(safe_strtou32); + + if (type_ == TYPE_DOUBLE) + return FloatingPointToIntConvertAndCheck<uint32_t, double>(double_); + + if (type_ == TYPE_FLOAT) + return FloatingPointToIntConvertAndCheck<uint32_t, float>(float_); + + return GenericConvert<uint32_t>(); +} + +util::StatusOr<int64_t> DataPiece::ToInt64() const { + if (type_ == TYPE_STRING) + return StringToNumber<int64_t>(safe_strto64); + + if (type_ == TYPE_DOUBLE) + return FloatingPointToIntConvertAndCheck<int64_t, double>(double_); + + if (type_ == TYPE_FLOAT) + return FloatingPointToIntConvertAndCheck<int64_t, float>(float_); + + return GenericConvert<int64_t>(); +} + +util::StatusOr<uint64_t> DataPiece::ToUint64() const { + if (type_ == TYPE_STRING) + return StringToNumber<uint64_t>(safe_strtou64); + + if (type_ == TYPE_DOUBLE) + return FloatingPointToIntConvertAndCheck<uint64_t, double>(double_); + + if (type_ == TYPE_FLOAT) + return FloatingPointToIntConvertAndCheck<uint64_t, float>(float_); + + return GenericConvert<uint64_t>(); +} + +util::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(); + util::StatusOr<double> value = StringToNumber<double>(safe_strtod); + if (value.ok() && !std::isfinite(value.value())) { + // safe_strtod converts out-of-range values to +inf/-inf, but we want + // to report them as errors. + return util::InvalidArgumentError(StrCat("\"", str_, "\"")); + } else { + return value; + } + } + return GenericConvert<double>(); +} + +util::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>(); +} + +util::StatusOr<bool> DataPiece::ToBool() const { + switch (type_) { + case TYPE_BOOL: + return bool_; + case TYPE_STRING: + return StringToNumber<bool>(safe_strtob); + default: + return util::InvalidArgumentError( + ValueAsStringOrDefault("Wrong type. Cannot convert to Bool.")); + } +} + +util::StatusOr<std::string> DataPiece::ToString() const { + switch (type_) { + case TYPE_STRING: + return std::string(str_); + case TYPE_BYTES: { + std::string base64; + Base64Escape(str_, &base64); + return base64; + } + default: + return util::InvalidArgumentError( + ValueAsStringOrDefault("Cannot convert to string.")); + } +} + +std::string DataPiece::ValueAsStringOrDefault( + StringPiece default_string) const { + switch (type_) { + case TYPE_INT32: + return StrCat(i32_); + case TYPE_INT64: + return StrCat(i64_); + case TYPE_UINT32: + return StrCat(u32_); + case TYPE_UINT64: + return StrCat(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: { + std::string base64; + WebSafeBase64Escape(str_, &base64); + return StrCat("\"", base64, "\""); + } + case TYPE_NULL: + return "null"; + default: + return std::string(default_string); + } +} + +util::StatusOr<std::string> DataPiece::ToBytes() const { + if (type_ == TYPE_BYTES) return str_.ToString(); + if (type_ == TYPE_STRING) { + std::string decoded; + if (!DecodeBase64(str_, &decoded)) { + return util::InvalidArgumentError( + ValueAsStringOrDefault("Invalid data in input.")); + } + return decoded; + } else { + return util::InvalidArgumentError(ValueAsStringOrDefault( + "Wrong type. Only String or Bytes can be converted to Bytes.")); + } +} + +util::StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type, + bool use_lower_camel_for_enums, + bool case_insensitive_enum_parsing, + 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. + std::string enum_name = std::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. + util::StatusOr<int32_t> int_value = ToInt32(); + if (int_value.ok()) { + if (const google::protobuf::EnumValue* enum_value = + FindEnumValueByNumberOrNull(enum_type, int_value.value())) { + return enum_value->number(); + } + } + + // Next try a normalized name. + bool should_normalize_enum = + case_insensitive_enum_parsing || use_lower_camel_for_enums; + if (should_normalize_enum) { + for (std::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; + if (enum_type->enumvalue_size() > 0) { + 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 util::InvalidArgumentError( + ValueAsStringOrDefault("Cannot find enum with given value.")); +} + +template <typename To> +util::StatusOr<To> DataPiece::GenericConvert() const { + switch (type_) { + case TYPE_INT32: + return NumberConvertAndCheck<To, int32_t>(i32_); + case TYPE_INT64: + return NumberConvertAndCheck<To, int64_t>(i64_); + case TYPE_UINT32: + return NumberConvertAndCheck<To, uint32_t>(u32_); + case TYPE_UINT64: + return NumberConvertAndCheck<To, uint64_t>(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 util::InvalidArgumentError(ValueAsStringOrDefault( + "Wrong type. Bool, Enum, String and Cord not supported in " + "GenericConvert.")); + } +} + +template <typename To> +util::StatusOr<To> DataPiece::StringToNumber(bool (*func)(StringPiece, + To*)) const { + if (str_.size() > 0 && (str_[0] == ' ' || str_[str_.size() - 1] == ' ')) { + return util::InvalidArgumentError(StrCat("\"", str_, "\"")); + } + To result; + if (func(str_, &result)) return result; + return util::InvalidArgumentError( + StrCat("\"", std::string(str_), "\"")); +} + +bool DataPiece::DecodeBase64(StringPiece src, std::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. + std::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, HasSuffixString(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_) { + std::string encoded; + Base64Escape(reinterpret_cast<const unsigned char*>(dest->data()), + dest->length(), &encoded, false); + StringPiece src_no_padding = StringPiece(src).substr( + 0, HasSuffixString(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 |