OLD | NEW |
1 // Protocol Buffers - Google's data interchange format | 1 // Protocol Buffers - Google's data interchange format |
2 // Copyright 2008 Google Inc. All rights reserved. | 2 // Copyright 2008 Google Inc. All rights reserved. |
3 // https://developers.google.com/protocol-buffers/ | 3 // https://developers.google.com/protocol-buffers/ |
4 // | 4 // |
5 // Redistribution and use in source and binary forms, with or without | 5 // Redistribution and use in source and binary forms, with or without |
6 // modification, are permitted provided that the following conditions are | 6 // modification, are permitted provided that the following conditions are |
7 // met: | 7 // met: |
8 // | 8 // |
9 // * Redistributions of source code must retain the above copyright | 9 // * Redistributions of source code must retain the above copyright |
10 // notice, this list of conditions and the following disclaimer. | 10 // notice, this list of conditions and the following disclaimer. |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
87 // point types (double, float) only. | 87 // point types (double, float) only. |
88 template <typename To, typename From> | 88 template <typename To, typename From> |
89 StatusOr<To> FloatingPointToIntConvertAndCheck(From before) { | 89 StatusOr<To> FloatingPointToIntConvertAndCheck(From before) { |
90 if (::google::protobuf::internal::is_same<From, To>::value) return before; | 90 if (::google::protobuf::internal::is_same<From, To>::value) return before; |
91 | 91 |
92 To after = static_cast<To>(before); | 92 To after = static_cast<To>(before); |
93 return ValidateNumberConversion(after, before); | 93 return ValidateNumberConversion(after, before); |
94 } | 94 } |
95 | 95 |
96 // For conversion between double and float only. | 96 // For conversion between double and float only. |
97 StatusOr<double> FloatToDouble(float before) { | 97 template <typename To, typename From> |
98 // Casting float to double should just work as double has more precision | 98 StatusOr<To> FloatingPointConvertAndCheck(From before) { |
99 // than float. | 99 if (MathLimits<From>::IsNaN(before)) { |
100 return static_cast<double>(before); | 100 return std::numeric_limits<To>::quiet_NaN(); |
101 } | 101 } |
102 | 102 |
103 StatusOr<float> DoubleToFloat(double before) { | 103 To after = static_cast<To>(before); |
104 if (MathLimits<double>::IsNaN(before)) { | 104 if (MathUtil::AlmostEquals<To>(after, before)) { |
105 return std::numeric_limits<float>::quiet_NaN(); | 105 return after; |
106 } else if (!MathLimits<double>::IsFinite(before)) { | |
107 // Converting a double +inf/-inf to float should just work. | |
108 return static_cast<float>(before); | |
109 } else if (before > std::numeric_limits<float>::max() || | |
110 before < -std::numeric_limits<float>::max()) { | |
111 // Double value outside of the range of float. | |
112 return InvalidArgument(DoubleAsString(before)); | |
113 } else { | 106 } else { |
114 return static_cast<float>(before); | 107 return InvalidArgument(::google::protobuf::internal::is_same<From, double>::
value |
| 108 ? DoubleAsString(before) |
| 109 : FloatAsString(before)); |
115 } | 110 } |
116 } | 111 } |
117 | 112 |
118 } // namespace | 113 } // namespace |
119 | 114 |
120 StatusOr<int32> DataPiece::ToInt32() const { | 115 StatusOr<int32> DataPiece::ToInt32() const { |
121 if (type_ == TYPE_STRING) return StringToNumber<int32>(safe_strto32); | 116 if (type_ == TYPE_STRING) return StringToNumber<int32>(safe_strto32); |
122 | 117 |
123 if (type_ == TYPE_DOUBLE) | 118 if (type_ == TYPE_DOUBLE) |
124 return FloatingPointToIntConvertAndCheck<int32, double>(double_); | 119 return FloatingPointToIntConvertAndCheck<int32, double>(double_); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
160 return FloatingPointToIntConvertAndCheck<uint64, double>(double_); | 155 return FloatingPointToIntConvertAndCheck<uint64, double>(double_); |
161 | 156 |
162 if (type_ == TYPE_FLOAT) | 157 if (type_ == TYPE_FLOAT) |
163 return FloatingPointToIntConvertAndCheck<uint64, float>(float_); | 158 return FloatingPointToIntConvertAndCheck<uint64, float>(float_); |
164 | 159 |
165 return GenericConvert<uint64>(); | 160 return GenericConvert<uint64>(); |
166 } | 161 } |
167 | 162 |
168 StatusOr<double> DataPiece::ToDouble() const { | 163 StatusOr<double> DataPiece::ToDouble() const { |
169 if (type_ == TYPE_FLOAT) { | 164 if (type_ == TYPE_FLOAT) { |
170 return FloatToDouble(float_); | 165 return FloatingPointConvertAndCheck<double, float>(float_); |
171 } | 166 } |
172 if (type_ == TYPE_STRING) { | 167 if (type_ == TYPE_STRING) { |
173 if (str_ == "Infinity") return std::numeric_limits<double>::infinity(); | 168 if (str_ == "Infinity") return std::numeric_limits<double>::infinity(); |
174 if (str_ == "-Infinity") return -std::numeric_limits<double>::infinity(); | 169 if (str_ == "-Infinity") return -std::numeric_limits<double>::infinity(); |
175 if (str_ == "NaN") return std::numeric_limits<double>::quiet_NaN(); | 170 if (str_ == "NaN") return std::numeric_limits<double>::quiet_NaN(); |
176 StatusOr<double> value = StringToNumber<double>(safe_strtod); | 171 return StringToNumber<double>(safe_strtod); |
177 if (value.ok() && !MathLimits<double>::IsFinite(value.ValueOrDie())) { | |
178 // safe_strtod converts out-of-range values to +inf/-inf, but we want | |
179 // to report them as errors. | |
180 return InvalidArgument(StrCat("\"", str_, "\"")); | |
181 } else { | |
182 return value; | |
183 } | |
184 } | 172 } |
185 return GenericConvert<double>(); | 173 return GenericConvert<double>(); |
186 } | 174 } |
187 | 175 |
188 StatusOr<float> DataPiece::ToFloat() const { | 176 StatusOr<float> DataPiece::ToFloat() const { |
189 if (type_ == TYPE_DOUBLE) { | 177 if (type_ == TYPE_DOUBLE) { |
190 return DoubleToFloat(double_); | 178 return FloatingPointConvertAndCheck<float, double>(double_); |
191 } | 179 } |
192 if (type_ == TYPE_STRING) { | 180 if (type_ == TYPE_STRING) { |
193 if (str_ == "Infinity") return std::numeric_limits<float>::infinity(); | 181 if (str_ == "Infinity") return std::numeric_limits<float>::infinity(); |
194 if (str_ == "-Infinity") return -std::numeric_limits<float>::infinity(); | 182 if (str_ == "-Infinity") return -std::numeric_limits<float>::infinity(); |
195 if (str_ == "NaN") return std::numeric_limits<float>::quiet_NaN(); | 183 if (str_ == "NaN") return std::numeric_limits<float>::quiet_NaN(); |
196 // SafeStrToFloat() is used instead of safe_strtof() because the later | 184 // SafeStrToFloat() is used instead of safe_strtof() because the later |
197 // does not fail on inputs like SimpleDtoa(DBL_MAX). | 185 // does not fail on inputs like SimpleDtoa(DBL_MAX). |
198 return StringToNumber<float>(SafeStrToFloat); | 186 return StringToNumber<float>(SafeStrToFloat); |
199 } | 187 } |
200 return GenericConvert<float>(); | 188 return GenericConvert<float>(); |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
264 if (!DecodeBase64(str_, &decoded)) { | 252 if (!DecodeBase64(str_, &decoded)) { |
265 return InvalidArgument(ValueAsStringOrDefault("Invalid data in input.")); | 253 return InvalidArgument(ValueAsStringOrDefault("Invalid data in input.")); |
266 } | 254 } |
267 return decoded; | 255 return decoded; |
268 } else { | 256 } else { |
269 return InvalidArgument(ValueAsStringOrDefault( | 257 return InvalidArgument(ValueAsStringOrDefault( |
270 "Wrong type. Only String or Bytes can be converted to Bytes.")); | 258 "Wrong type. Only String or Bytes can be converted to Bytes.")); |
271 } | 259 } |
272 } | 260 } |
273 | 261 |
274 StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type, | 262 StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type) const { |
275 bool use_lower_camel_for_enums) const { | |
276 if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE; | 263 if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE; |
277 | 264 |
278 if (type_ == TYPE_STRING) { | 265 if (type_ == TYPE_STRING) { |
279 // First try the given value as a name. | 266 // First try the given value as a name. |
280 string enum_name = str_.ToString(); | 267 string enum_name = str_.ToString(); |
281 const google::protobuf::EnumValue* value = | 268 const google::protobuf::EnumValue* value = |
282 FindEnumValueByNameOrNull(enum_type, enum_name); | 269 FindEnumValueByNameOrNull(enum_type, enum_name); |
283 if (value != NULL) return value->number(); | 270 if (value != NULL) return value->number(); |
284 | |
285 // Check if int version of enum is sent as string. | |
286 StatusOr<int32> int_value = ToInt32(); | |
287 if (int_value.ok()) { | |
288 if (const google::protobuf::EnumValue* enum_value = | |
289 FindEnumValueByNumberOrNull(enum_type, int_value.ValueOrDie())) { | |
290 return enum_value->number(); | |
291 } | |
292 } | |
293 | |
294 // Next try a normalized name. | 271 // Next try a normalized name. |
295 for (string::iterator it = enum_name.begin(); it != enum_name.end(); ++it) { | 272 for (string::iterator it = enum_name.begin(); it != enum_name.end(); ++it) { |
296 *it = *it == '-' ? '_' : ascii_toupper(*it); | 273 *it = *it == '-' ? '_' : ascii_toupper(*it); |
297 } | 274 } |
298 value = FindEnumValueByNameOrNull(enum_type, enum_name); | 275 value = FindEnumValueByNameOrNull(enum_type, enum_name); |
299 if (value != NULL) return value->number(); | 276 if (value != NULL) return value->number(); |
300 | 277 } else { |
301 // If use_lower_camel_for_enums is true try with enum name without | 278 StatusOr<int32> value = ToInt32(); |
302 // underscore. This will also accept camel case names as the enum_name has | 279 if (value.ok()) { |
303 // been normalized before. | 280 if (const google::protobuf::EnumValue* enum_value = |
304 if (use_lower_camel_for_enums) { | 281 FindEnumValueByNumberOrNull(enum_type, value.ValueOrDie())) { |
305 value = FindEnumValueByNameWithoutUnderscoreOrNull(enum_type, enum_name); | 282 return enum_value->number(); |
306 if (value != NULL) return value->number(); | 283 } |
307 } | 284 } |
308 } else { | |
309 // We don't need to check whether the value is actually declared in the | |
310 // enum because we preserve unknown enum values as well. | |
311 return ToInt32(); | |
312 } | 285 } |
313 return InvalidArgument( | 286 return InvalidArgument( |
314 ValueAsStringOrDefault("Cannot find enum with given value.")); | 287 ValueAsStringOrDefault("Cannot find enum with given value.")); |
315 } | 288 } |
316 | 289 |
317 template <typename To> | 290 template <typename To> |
318 StatusOr<To> DataPiece::GenericConvert() const { | 291 StatusOr<To> DataPiece::GenericConvert() const { |
319 switch (type_) { | 292 switch (type_) { |
320 case TYPE_INT32: | 293 case TYPE_INT32: |
321 return NumberConvertAndCheck<To, int32>(i32_); | 294 return NumberConvertAndCheck<To, int32>(i32_); |
(...skipping 27 matching lines...) Expand all Loading... |
349 bool DataPiece::DecodeBase64(StringPiece src, string* dest) const { | 322 bool DataPiece::DecodeBase64(StringPiece src, string* dest) const { |
350 // Try web-safe decode first, if it fails, try the non-web-safe decode. | 323 // Try web-safe decode first, if it fails, try the non-web-safe decode. |
351 if (WebSafeBase64Unescape(src, dest)) { | 324 if (WebSafeBase64Unescape(src, dest)) { |
352 if (use_strict_base64_decoding_) { | 325 if (use_strict_base64_decoding_) { |
353 // In strict mode, check if the escaped version gives us the same value as | 326 // In strict mode, check if the escaped version gives us the same value as |
354 // unescaped. | 327 // unescaped. |
355 string encoded; | 328 string encoded; |
356 // WebSafeBase64Escape does no padding by default. | 329 // WebSafeBase64Escape does no padding by default. |
357 WebSafeBase64Escape(*dest, &encoded); | 330 WebSafeBase64Escape(*dest, &encoded); |
358 // Remove trailing padding '=' characters before comparison. | 331 // Remove trailing padding '=' characters before comparison. |
359 StringPiece src_no_padding = StringPiece(src).substr( | 332 StringPiece src_no_padding(src, 0, src.ends_with("=") |
360 0, src.ends_with("=") ? src.find_last_not_of('=') + 1 : src.length()); | 333 ? src.find_last_not_of('=') + 1 |
| 334 : src.length()); |
361 return encoded == src_no_padding; | 335 return encoded == src_no_padding; |
362 } | 336 } |
363 return true; | 337 return true; |
364 } | 338 } |
365 | 339 |
366 if (Base64Unescape(src, dest)) { | 340 if (Base64Unescape(src, dest)) { |
367 if (use_strict_base64_decoding_) { | 341 if (use_strict_base64_decoding_) { |
368 string encoded; | 342 string encoded; |
369 Base64Escape( | 343 Base64Escape( |
370 reinterpret_cast<const unsigned char*>(dest->data()), dest->length(), | 344 reinterpret_cast<const unsigned char*>(dest->data()), dest->length(), |
371 &encoded, false); | 345 &encoded, false); |
372 StringPiece src_no_padding = StringPiece(src).substr( | 346 StringPiece src_no_padding(src, 0, src.ends_with("=") |
373 0, src.ends_with("=") ? src.find_last_not_of('=') + 1 : src.length()); | 347 ? src.find_last_not_of('=') + 1 |
| 348 : src.length()); |
374 return encoded == src_no_padding; | 349 return encoded == src_no_padding; |
375 } | 350 } |
376 return true; | 351 return true; |
377 } | 352 } |
378 | 353 |
379 return false; | 354 return false; |
380 } | 355 } |
381 | 356 |
382 void DataPiece::InternalCopy(const DataPiece& other) { | |
383 type_ = other.type_; | |
384 use_strict_base64_decoding_ = other.use_strict_base64_decoding_; | |
385 switch (type_) { | |
386 case TYPE_INT32: | |
387 case TYPE_INT64: | |
388 case TYPE_UINT32: | |
389 case TYPE_UINT64: | |
390 case TYPE_DOUBLE: | |
391 case TYPE_FLOAT: | |
392 case TYPE_BOOL: | |
393 case TYPE_ENUM: | |
394 case TYPE_NULL: | |
395 case TYPE_BYTES: | |
396 case TYPE_STRING: { | |
397 str_ = other.str_; | |
398 break; | |
399 } | |
400 } | |
401 } | |
402 | |
403 } // namespace converter | 357 } // namespace converter |
404 } // namespace util | 358 } // namespace util |
405 } // namespace protobuf | 359 } // namespace protobuf |
406 } // namespace google | 360 } // namespace google |
OLD | NEW |