• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <cmath>
34 #include <limits>
35 
36 #include <google/protobuf/struct.pb.h>
37 #include <google/protobuf/type.pb.h>
38 #include <google/protobuf/descriptor.h>
39 #include <google/protobuf/util/internal/utility.h>
40 #include <google/protobuf/stubs/strutil.h>
41 #include <google/protobuf/stubs/mathutil.h>
42 
43 namespace google {
44 namespace protobuf {
45 namespace util {
46 namespace converter {
47 
48 using util::Status;
49 using util::StatusOr;
50 using util::error::Code;
51 
52 namespace {
53 
InvalidArgument(StringPiece value_str)54 inline Status InvalidArgument(StringPiece value_str) {
55   return Status(util::error::INVALID_ARGUMENT, value_str);
56 }
57 
58 template <typename To, typename From>
ValidateNumberConversion(To after,From before)59 StatusOr<To> ValidateNumberConversion(To after, From before) {
60   if (after == before &&
61       MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) {
62     return after;
63   } else {
64     return InvalidArgument(std::is_integral<From>::value
65                                ? ValueAsString(before)
66                                : std::is_same<From, double>::value
67                                      ? DoubleAsString(before)
68                                      : FloatAsString(before));
69   }
70 }
71 
72 // For general conversion between
73 //     int32, int64, uint32, uint64, double and float
74 // except conversion between double and float.
75 template <typename To, typename From>
NumberConvertAndCheck(From before)76 StatusOr<To> NumberConvertAndCheck(From before) {
77   if (std::is_same<From, To>::value) return before;
78 
79   To after = static_cast<To>(before);
80   return ValidateNumberConversion(after, before);
81 }
82 
83 // For conversion to integer types (int32, int64, uint32, uint64) from floating
84 // point types (double, float) only.
85 template <typename To, typename From>
FloatingPointToIntConvertAndCheck(From before)86 StatusOr<To> FloatingPointToIntConvertAndCheck(From before) {
87   if (std::is_same<From, To>::value) return before;
88 
89   To after = static_cast<To>(before);
90   return ValidateNumberConversion(after, before);
91 }
92 
93 // For conversion between double and float only.
FloatToDouble(float before)94 StatusOr<double> FloatToDouble(float before) {
95   // Casting float to double should just work as double has more precision
96   // than float.
97   return static_cast<double>(before);
98 }
99 
DoubleToFloat(double before)100 StatusOr<float> DoubleToFloat(double before) {
101   if (std::isnan(before)) {
102     return std::numeric_limits<float>::quiet_NaN();
103   } else if (!std::isfinite(before)) {
104     // Converting a double +inf/-inf to float should just work.
105     return static_cast<float>(before);
106   } else if (before > std::numeric_limits<float>::max() ||
107              before < -std::numeric_limits<float>::max()) {
108     // Double value outside of the range of float.
109     return InvalidArgument(DoubleAsString(before));
110   } else {
111     return static_cast<float>(before);
112   }
113 }
114 
115 }  // namespace
116 
ToInt32() const117 StatusOr<int32> DataPiece::ToInt32() const {
118   if (type_ == TYPE_STRING) return StringToNumber<int32>(safe_strto32);
119 
120   if (type_ == TYPE_DOUBLE)
121     return FloatingPointToIntConvertAndCheck<int32, double>(double_);
122 
123   if (type_ == TYPE_FLOAT)
124     return FloatingPointToIntConvertAndCheck<int32, float>(float_);
125 
126   return GenericConvert<int32>();
127 }
128 
ToUint32() const129 StatusOr<uint32> DataPiece::ToUint32() const {
130   if (type_ == TYPE_STRING)
131     return StringToNumber<uint32>(safe_strtou32);
132 
133   if (type_ == TYPE_DOUBLE)
134     return FloatingPointToIntConvertAndCheck<uint32, double>(double_);
135 
136   if (type_ == TYPE_FLOAT)
137     return FloatingPointToIntConvertAndCheck<uint32, float>(float_);
138 
139   return GenericConvert<uint32>();
140 }
141 
ToInt64() const142 StatusOr<int64> DataPiece::ToInt64() const {
143   if (type_ == TYPE_STRING) return StringToNumber<int64>(safe_strto64);
144 
145   if (type_ == TYPE_DOUBLE)
146     return FloatingPointToIntConvertAndCheck<int64, double>(double_);
147 
148   if (type_ == TYPE_FLOAT)
149     return FloatingPointToIntConvertAndCheck<int64, float>(float_);
150 
151   return GenericConvert<int64>();
152 }
153 
ToUint64() const154 StatusOr<uint64> DataPiece::ToUint64() const {
155   if (type_ == TYPE_STRING)
156     return StringToNumber<uint64>(safe_strtou64);
157 
158   if (type_ == TYPE_DOUBLE)
159     return FloatingPointToIntConvertAndCheck<uint64, double>(double_);
160 
161   if (type_ == TYPE_FLOAT)
162     return FloatingPointToIntConvertAndCheck<uint64, float>(float_);
163 
164   return GenericConvert<uint64>();
165 }
166 
ToDouble() const167 StatusOr<double> DataPiece::ToDouble() const {
168   if (type_ == TYPE_FLOAT) {
169     return FloatToDouble(float_);
170   }
171   if (type_ == TYPE_STRING) {
172     if (str_ == "Infinity") return std::numeric_limits<double>::infinity();
173     if (str_ == "-Infinity") return -std::numeric_limits<double>::infinity();
174     if (str_ == "NaN") return std::numeric_limits<double>::quiet_NaN();
175     StatusOr<double> value = StringToNumber<double>(safe_strtod);
176     if (value.ok() && !std::isfinite(value.value())) {
177       // safe_strtod converts out-of-range values to +inf/-inf, but we want
178       // to report them as errors.
179       return InvalidArgument(StrCat("\"", str_, "\""));
180     } else {
181       return value;
182     }
183   }
184   return GenericConvert<double>();
185 }
186 
ToFloat() const187 StatusOr<float> DataPiece::ToFloat() const {
188   if (type_ == TYPE_DOUBLE) {
189     return DoubleToFloat(double_);
190   }
191   if (type_ == TYPE_STRING) {
192     if (str_ == "Infinity") return std::numeric_limits<float>::infinity();
193     if (str_ == "-Infinity") return -std::numeric_limits<float>::infinity();
194     if (str_ == "NaN") return std::numeric_limits<float>::quiet_NaN();
195     // SafeStrToFloat() is used instead of safe_strtof() because the later
196     // does not fail on inputs like SimpleDtoa(DBL_MAX).
197     return StringToNumber<float>(SafeStrToFloat);
198   }
199   return GenericConvert<float>();
200 }
201 
ToBool() const202 StatusOr<bool> DataPiece::ToBool() const {
203   switch (type_) {
204     case TYPE_BOOL:
205       return bool_;
206     case TYPE_STRING:
207       return StringToNumber<bool>(safe_strtob);
208     default:
209       return InvalidArgument(
210           ValueAsStringOrDefault("Wrong type. Cannot convert to Bool."));
211   }
212 }
213 
ToString() const214 StatusOr<std::string> DataPiece::ToString() const {
215   switch (type_) {
216     case TYPE_STRING:
217       return std::string(str_);
218     case TYPE_BYTES: {
219       std::string base64;
220       Base64Escape(str_, &base64);
221       return base64;
222     }
223     default:
224       return InvalidArgument(
225           ValueAsStringOrDefault("Cannot convert to string."));
226   }
227 }
228 
ValueAsStringOrDefault(StringPiece default_string) const229 std::string DataPiece::ValueAsStringOrDefault(
230     StringPiece default_string) const {
231   switch (type_) {
232     case TYPE_INT32:
233       return StrCat(i32_);
234     case TYPE_INT64:
235       return StrCat(i64_);
236     case TYPE_UINT32:
237       return StrCat(u32_);
238     case TYPE_UINT64:
239       return StrCat(u64_);
240     case TYPE_DOUBLE:
241       return DoubleAsString(double_);
242     case TYPE_FLOAT:
243       return FloatAsString(float_);
244     case TYPE_BOOL:
245       return SimpleBtoa(bool_);
246     case TYPE_STRING:
247       return StrCat("\"", str_.ToString(), "\"");
248     case TYPE_BYTES: {
249       std::string base64;
250       WebSafeBase64Escape(str_, &base64);
251       return StrCat("\"", base64, "\"");
252     }
253     case TYPE_NULL:
254       return "null";
255     default:
256       return std::string(default_string);
257   }
258 }
259 
ToBytes() const260 StatusOr<std::string> DataPiece::ToBytes() const {
261   if (type_ == TYPE_BYTES) return str_.ToString();
262   if (type_ == TYPE_STRING) {
263     std::string decoded;
264     if (!DecodeBase64(str_, &decoded)) {
265       return InvalidArgument(ValueAsStringOrDefault("Invalid data in input."));
266     }
267     return decoded;
268   } else {
269     return InvalidArgument(ValueAsStringOrDefault(
270         "Wrong type. Only String or Bytes can be converted to Bytes."));
271   }
272 }
273 
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) const274 StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type,
275                                 bool use_lower_camel_for_enums,
276                                 bool case_insensitive_enum_parsing,
277                                 bool ignore_unknown_enum_values,
278                                 bool* is_unknown_enum_value) const {
279   if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE;
280 
281   if (type_ == TYPE_STRING) {
282     // First try the given value as a name.
283     std::string enum_name = std::string(str_);
284     const google::protobuf::EnumValue* value =
285         FindEnumValueByNameOrNull(enum_type, enum_name);
286     if (value != nullptr) return value->number();
287 
288     // Check if int version of enum is sent as string.
289     StatusOr<int32> int_value = ToInt32();
290     if (int_value.ok()) {
291       if (const google::protobuf::EnumValue* enum_value =
292               FindEnumValueByNumberOrNull(enum_type, int_value.value())) {
293         return enum_value->number();
294       }
295     }
296 
297     // Next try a normalized name.
298     bool should_normalize_enum =
299         case_insensitive_enum_parsing || use_lower_camel_for_enums;
300     if (should_normalize_enum) {
301       for (std::string::iterator it = enum_name.begin(); it != enum_name.end();
302            ++it) {
303         *it = *it == '-' ? '_' : ascii_toupper(*it);
304       }
305       value = FindEnumValueByNameOrNull(enum_type, enum_name);
306       if (value != nullptr) return value->number();
307     }
308 
309     // If use_lower_camel_for_enums is true try with enum name without
310     // underscore. This will also accept camel case names as the enum_name has
311     // been normalized before.
312     if (use_lower_camel_for_enums) {
313       value = FindEnumValueByNameWithoutUnderscoreOrNull(enum_type, enum_name);
314       if (value != nullptr) return value->number();
315     }
316 
317     // If ignore_unknown_enum_values is true an unknown enum value is ignored.
318     if (ignore_unknown_enum_values) {
319       *is_unknown_enum_value = true;
320       if (enum_type->enumvalue_size() > 0) {
321         return enum_type->enumvalue(0).number();
322       }
323     }
324   } else {
325     // We don't need to check whether the value is actually declared in the
326     // enum because we preserve unknown enum values as well.
327     return ToInt32();
328   }
329   return InvalidArgument(
330       ValueAsStringOrDefault("Cannot find enum with given value."));
331 }
332 
333 template <typename To>
GenericConvert() const334 StatusOr<To> DataPiece::GenericConvert() const {
335   switch (type_) {
336     case TYPE_INT32:
337       return NumberConvertAndCheck<To, int32>(i32_);
338     case TYPE_INT64:
339       return NumberConvertAndCheck<To, int64>(i64_);
340     case TYPE_UINT32:
341       return NumberConvertAndCheck<To, uint32>(u32_);
342     case TYPE_UINT64:
343       return NumberConvertAndCheck<To, uint64>(u64_);
344     case TYPE_DOUBLE:
345       return NumberConvertAndCheck<To, double>(double_);
346     case TYPE_FLOAT:
347       return NumberConvertAndCheck<To, float>(float_);
348     default:  // TYPE_ENUM, TYPE_STRING, TYPE_CORD, TYPE_BOOL
349       return InvalidArgument(ValueAsStringOrDefault(
350           "Wrong type. Bool, Enum, String and Cord not supported in "
351           "GenericConvert."));
352   }
353 }
354 
355 template <typename To>
StringToNumber(bool (* func)(StringPiece,To *)) const356 StatusOr<To> DataPiece::StringToNumber(bool (*func)(StringPiece,
357                                                     To*)) const {
358   if (str_.size() > 0 && (str_[0] == ' ' || str_[str_.size() - 1] == ' ')) {
359     return InvalidArgument(StrCat("\"", str_, "\""));
360   }
361   To result;
362   if (func(str_, &result)) return result;
363   return InvalidArgument(StrCat("\"", std::string(str_), "\""));
364 }
365 
DecodeBase64(StringPiece src,std::string * dest) const366 bool DataPiece::DecodeBase64(StringPiece src, std::string* dest) const {
367   // Try web-safe decode first, if it fails, try the non-web-safe decode.
368   if (WebSafeBase64Unescape(src, dest)) {
369     if (use_strict_base64_decoding_) {
370       // In strict mode, check if the escaped version gives us the same value as
371       // unescaped.
372       std::string encoded;
373       // WebSafeBase64Escape does no padding by default.
374       WebSafeBase64Escape(*dest, &encoded);
375       // Remove trailing padding '=' characters before comparison.
376       StringPiece src_no_padding = StringPiece(src).substr(
377           0, HasSuffixString(src, "=") ? src.find_last_not_of('=') + 1
378                                       : src.length());
379       return encoded == src_no_padding;
380     }
381     return true;
382   }
383 
384   if (Base64Unescape(src, dest)) {
385     if (use_strict_base64_decoding_) {
386       std::string encoded;
387       Base64Escape(reinterpret_cast<const unsigned char*>(dest->data()),
388                          dest->length(), &encoded, false);
389       StringPiece src_no_padding = StringPiece(src).substr(
390           0, HasSuffixString(src, "=") ? src.find_last_not_of('=') + 1
391                                       : src.length());
392       return encoded == src_no_padding;
393     }
394     return true;
395   }
396 
397   return false;
398 }
399 
InternalCopy(const DataPiece & other)400 void DataPiece::InternalCopy(const DataPiece& other) {
401   type_ = other.type_;
402   use_strict_base64_decoding_ = other.use_strict_base64_decoding_;
403   switch (type_) {
404     case TYPE_INT32:
405     case TYPE_INT64:
406     case TYPE_UINT32:
407     case TYPE_UINT64:
408     case TYPE_DOUBLE:
409     case TYPE_FLOAT:
410     case TYPE_BOOL:
411     case TYPE_ENUM:
412     case TYPE_NULL:
413     case TYPE_BYTES:
414     case TYPE_STRING: {
415       str_ = other.str_;
416       break;
417     }
418   }
419 }
420 
421 }  // namespace converter
422 }  // namespace util
423 }  // namespace protobuf
424 }  // namespace google
425