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 41 namespace google { 42 namespace protobuf { 43 namespace util { 44 namespace converter { 45 46 using google::protobuf::EnumDescriptor; 47 using google::protobuf::EnumValueDescriptor; 48 ; 49 ; 50 ; 51 using util::error::Code; 52 using util::Status; 53 using util::StatusOr; 54 55 namespace { 56 57 inline Status InvalidArgument(StringPiece value_str) { 58 return Status(util::error::INVALID_ARGUMENT, value_str); 59 } 60 61 template <typename To, typename From> 62 StatusOr<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. 78 template <typename To, typename From> 79 StatusOr<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. 88 template <typename To, typename From> 89 StatusOr<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. 97 template <typename To, typename From> 98 StatusOr<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 115 StatusOr<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 127 StatusOr<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 139 StatusOr<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 151 StatusOr<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 163 StatusOr<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 176 StatusOr<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 191 StatusOr<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 203 StatusOr<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 218 string 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 248 StatusOr<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 262 StatusOr<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 290 template <typename To> 291 StatusOr<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 312 template <typename To> 313 StatusOr<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 322 bool 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