1// Protocol Buffers - Google's data interchange format 2// Copyright 2008 Google Inc. All rights reserved. 3// https://developers.google.com/protocol-buffers/ 4// 5// Redistribution and use in source and binary forms, with or without 6// modification, are permitted provided that the following conditions are 7// met: 8// 9// * Redistributions of source code must retain the above copyright 10// notice, this list of conditions and the following disclaimer. 11// * Redistributions in binary form must reproduce the above 12// copyright notice, this list of conditions and the following disclaimer 13// in the documentation and/or other materials provided with the 14// distribution. 15// * Neither the name of Google Inc. nor the names of its 16// contributors may be used to endorse or promote products derived from 17// this software without specific prior written permission. 18// 19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31#include <google/protobuf/util/internal/datapiece.h> 32 33#include <google/protobuf/struct.pb.h> 34#include <google/protobuf/type.pb.h> 35#include <google/protobuf/descriptor.h> 36#include <google/protobuf/util/internal/utility.h> 37#include <google/protobuf/stubs/strutil.h> 38#include <google/protobuf/stubs/mathlimits.h> 39#include <google/protobuf/stubs/mathutil.h> 40 41namespace google { 42namespace protobuf { 43namespace util { 44namespace converter { 45 46using google::protobuf::EnumDescriptor; 47using google::protobuf::EnumValueDescriptor; 48; 49; 50; 51using util::error::Code; 52using util::Status; 53using util::StatusOr; 54 55namespace { 56 57inline Status InvalidArgument(StringPiece value_str) { 58 return Status(util::error::INVALID_ARGUMENT, value_str); 59} 60 61template <typename To, typename From> 62StatusOr<To> ValidateNumberConversion(To after, From before) { 63 if (after == before && 64 MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) { 65 return after; 66 } else { 67 return InvalidArgument(::google::protobuf::internal::is_integral<From>::value 68 ? ValueAsString(before) 69 : ::google::protobuf::internal::is_same<From, double>::value 70 ? DoubleAsString(before) 71 : FloatAsString(before)); 72 } 73} 74 75// For general conversion between 76// int32, int64, uint32, uint64, double and float 77// except conversion between double and float. 78template <typename To, typename From> 79StatusOr<To> NumberConvertAndCheck(From before) { 80 if (::google::protobuf::internal::is_same<From, To>::value) return before; 81 82 To after = static_cast<To>(before); 83 return ValidateNumberConversion(after, before); 84} 85 86// For conversion to integer types (int32, int64, uint32, uint64) from floating 87// point types (double, float) only. 88template <typename To, typename From> 89StatusOr<To> FloatingPointToIntConvertAndCheck(From before) { 90 if (::google::protobuf::internal::is_same<From, To>::value) return before; 91 92 To after = static_cast<To>(before); 93 return ValidateNumberConversion(after, before); 94} 95 96// For conversion between double and float only. 97template <typename To, typename From> 98StatusOr<To> FloatingPointConvertAndCheck(From before) { 99 if (MathLimits<From>::IsNaN(before)) { 100 return std::numeric_limits<To>::quiet_NaN(); 101 } 102 103 To after = static_cast<To>(before); 104 if (MathUtil::AlmostEquals<To>(after, before)) { 105 return after; 106 } else { 107 return InvalidArgument(::google::protobuf::internal::is_same<From, double>::value 108 ? DoubleAsString(before) 109 : FloatAsString(before)); 110 } 111} 112 113} // namespace 114 115StatusOr<int32> DataPiece::ToInt32() const { 116 if (type_ == TYPE_STRING) return StringToNumber<int32>(safe_strto32); 117 118 if (type_ == TYPE_DOUBLE) 119 return FloatingPointToIntConvertAndCheck<int32, double>(double_); 120 121 if (type_ == TYPE_FLOAT) 122 return FloatingPointToIntConvertAndCheck<int32, float>(float_); 123 124 return GenericConvert<int32>(); 125} 126 127StatusOr<uint32> DataPiece::ToUint32() const { 128 if (type_ == TYPE_STRING) return StringToNumber<uint32>(safe_strtou32); 129 130 if (type_ == TYPE_DOUBLE) 131 return FloatingPointToIntConvertAndCheck<uint32, double>(double_); 132 133 if (type_ == TYPE_FLOAT) 134 return FloatingPointToIntConvertAndCheck<uint32, float>(float_); 135 136 return GenericConvert<uint32>(); 137} 138 139StatusOr<int64> DataPiece::ToInt64() const { 140 if (type_ == TYPE_STRING) return StringToNumber<int64>(safe_strto64); 141 142 if (type_ == TYPE_DOUBLE) 143 return FloatingPointToIntConvertAndCheck<int64, double>(double_); 144 145 if (type_ == TYPE_FLOAT) 146 return FloatingPointToIntConvertAndCheck<int64, float>(float_); 147 148 return GenericConvert<int64>(); 149} 150 151StatusOr<uint64> DataPiece::ToUint64() const { 152 if (type_ == TYPE_STRING) return StringToNumber<uint64>(safe_strtou64); 153 154 if (type_ == TYPE_DOUBLE) 155 return FloatingPointToIntConvertAndCheck<uint64, double>(double_); 156 157 if (type_ == TYPE_FLOAT) 158 return FloatingPointToIntConvertAndCheck<uint64, float>(float_); 159 160 return GenericConvert<uint64>(); 161} 162 163StatusOr<double> DataPiece::ToDouble() const { 164 if (type_ == TYPE_FLOAT) { 165 return FloatingPointConvertAndCheck<double, float>(float_); 166 } 167 if (type_ == TYPE_STRING) { 168 if (str_ == "Infinity") return std::numeric_limits<double>::infinity(); 169 if (str_ == "-Infinity") return -std::numeric_limits<double>::infinity(); 170 if (str_ == "NaN") return std::numeric_limits<double>::quiet_NaN(); 171 return StringToNumber<double>(safe_strtod); 172 } 173 return GenericConvert<double>(); 174} 175 176StatusOr<float> DataPiece::ToFloat() const { 177 if (type_ == TYPE_DOUBLE) { 178 return FloatingPointConvertAndCheck<float, double>(double_); 179 } 180 if (type_ == TYPE_STRING) { 181 if (str_ == "Infinity") return std::numeric_limits<float>::infinity(); 182 if (str_ == "-Infinity") return -std::numeric_limits<float>::infinity(); 183 if (str_ == "NaN") return std::numeric_limits<float>::quiet_NaN(); 184 // SafeStrToFloat() is used instead of safe_strtof() because the later 185 // does not fail on inputs like SimpleDtoa(DBL_MAX). 186 return StringToNumber<float>(SafeStrToFloat); 187 } 188 return GenericConvert<float>(); 189} 190 191StatusOr<bool> DataPiece::ToBool() const { 192 switch (type_) { 193 case TYPE_BOOL: 194 return bool_; 195 case TYPE_STRING: 196 return StringToNumber<bool>(safe_strtob); 197 default: 198 return InvalidArgument( 199 ValueAsStringOrDefault("Wrong type. Cannot convert to Bool.")); 200 } 201} 202 203StatusOr<string> DataPiece::ToString() const { 204 switch (type_) { 205 case TYPE_STRING: 206 return str_.ToString(); 207 case TYPE_BYTES: { 208 string base64; 209 Base64Escape(str_, &base64); 210 return base64; 211 } 212 default: 213 return InvalidArgument( 214 ValueAsStringOrDefault("Cannot convert to string.")); 215 } 216} 217 218string DataPiece::ValueAsStringOrDefault(StringPiece default_string) const { 219 switch (type_) { 220 case TYPE_INT32: 221 return SimpleItoa(i32_); 222 case TYPE_INT64: 223 return SimpleItoa(i64_); 224 case TYPE_UINT32: 225 return SimpleItoa(u32_); 226 case TYPE_UINT64: 227 return SimpleItoa(u64_); 228 case TYPE_DOUBLE: 229 return DoubleAsString(double_); 230 case TYPE_FLOAT: 231 return FloatAsString(float_); 232 case TYPE_BOOL: 233 return SimpleBtoa(bool_); 234 case TYPE_STRING: 235 return StrCat("\"", str_.ToString(), "\""); 236 case TYPE_BYTES: { 237 string base64; 238 WebSafeBase64Escape(str_, &base64); 239 return StrCat("\"", base64, "\""); 240 } 241 case TYPE_NULL: 242 return "null"; 243 default: 244 return default_string.ToString(); 245 } 246} 247 248StatusOr<string> DataPiece::ToBytes() const { 249 if (type_ == TYPE_BYTES) return str_.ToString(); 250 if (type_ == TYPE_STRING) { 251 string decoded; 252 if (!DecodeBase64(str_, &decoded)) { 253 return InvalidArgument(ValueAsStringOrDefault("Invalid data in input.")); 254 } 255 return decoded; 256 } else { 257 return InvalidArgument(ValueAsStringOrDefault( 258 "Wrong type. Only String or Bytes can be converted to Bytes.")); 259 } 260} 261 262StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type) const { 263 if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE; 264 265 if (type_ == TYPE_STRING) { 266 // First try the given value as a name. 267 string enum_name = str_.ToString(); 268 const google::protobuf::EnumValue* value = 269 FindEnumValueByNameOrNull(enum_type, enum_name); 270 if (value != NULL) return value->number(); 271 // Next try a normalized name. 272 for (string::iterator it = enum_name.begin(); it != enum_name.end(); ++it) { 273 *it = *it == '-' ? '_' : ascii_toupper(*it); 274 } 275 value = FindEnumValueByNameOrNull(enum_type, enum_name); 276 if (value != NULL) return value->number(); 277 } else { 278 StatusOr<int32> value = ToInt32(); 279 if (value.ok()) { 280 if (const google::protobuf::EnumValue* enum_value = 281 FindEnumValueByNumberOrNull(enum_type, value.ValueOrDie())) { 282 return enum_value->number(); 283 } 284 } 285 } 286 return InvalidArgument( 287 ValueAsStringOrDefault("Cannot find enum with given value.")); 288} 289 290template <typename To> 291StatusOr<To> DataPiece::GenericConvert() const { 292 switch (type_) { 293 case TYPE_INT32: 294 return NumberConvertAndCheck<To, int32>(i32_); 295 case TYPE_INT64: 296 return NumberConvertAndCheck<To, int64>(i64_); 297 case TYPE_UINT32: 298 return NumberConvertAndCheck<To, uint32>(u32_); 299 case TYPE_UINT64: 300 return NumberConvertAndCheck<To, uint64>(u64_); 301 case TYPE_DOUBLE: 302 return NumberConvertAndCheck<To, double>(double_); 303 case TYPE_FLOAT: 304 return NumberConvertAndCheck<To, float>(float_); 305 default: // TYPE_ENUM, TYPE_STRING, TYPE_CORD, TYPE_BOOL 306 return InvalidArgument(ValueAsStringOrDefault( 307 "Wrong type. Bool, Enum, String and Cord not supported in " 308 "GenericConvert.")); 309 } 310} 311 312template <typename To> 313StatusOr<To> DataPiece::StringToNumber(bool (*func)(StringPiece, To*)) const { 314 if (str_.size() > 0 && (str_[0] == ' ' || str_[str_.size() - 1] == ' ')) { 315 return InvalidArgument(StrCat("\"", str_, "\"")); 316 } 317 To result; 318 if (func(str_, &result)) return result; 319 return InvalidArgument(StrCat("\"", str_.ToString(), "\"")); 320} 321 322bool DataPiece::DecodeBase64(StringPiece src, string* dest) const { 323 // Try web-safe decode first, if it fails, try the non-web-safe decode. 324 if (WebSafeBase64Unescape(src, dest)) { 325 if (use_strict_base64_decoding_) { 326 // In strict mode, check if the escaped version gives us the same value as 327 // unescaped. 328 string encoded; 329 // WebSafeBase64Escape does no padding by default. 330 WebSafeBase64Escape(*dest, &encoded); 331 // Remove trailing padding '=' characters before comparison. 332 StringPiece src_no_padding(src, 0, src.ends_with("=") 333 ? src.find_last_not_of('=') + 1 334 : src.length()); 335 return encoded == src_no_padding; 336 } 337 return true; 338 } 339 340 if (Base64Unescape(src, dest)) { 341 if (use_strict_base64_decoding_) { 342 string encoded; 343 Base64Escape( 344 reinterpret_cast<const unsigned char*>(dest->data()), dest->length(), 345 &encoded, false); 346 StringPiece src_no_padding(src, 0, src.ends_with("=") 347 ? src.find_last_not_of('=') + 1 348 : src.length()); 349 return encoded == src_no_padding; 350 } 351 return true; 352 } 353 354 return false; 355} 356 357} // namespace converter 358} // namespace util 359} // namespace protobuf 360} // namespace google 361