mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
442 lines
15 KiB
C++
442 lines
15 KiB
C++
// 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 <thirdparty/protobuf/util/internal/datapiece.h>
|
|
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
|
|
#include <thirdparty/protobuf/struct.pb.h>
|
|
#include <thirdparty/protobuf/type.pb.h>
|
|
#include <thirdparty/protobuf/descriptor.h>
|
|
#include <thirdparty/protobuf/stubs/status.h>
|
|
#include <thirdparty/protobuf/stubs/strutil.h>
|
|
#include <thirdparty/protobuf/util/internal/utility.h>
|
|
#include <thirdparty/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
|