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.ValueOrDie())) {
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.ValueOrDie())) {
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       return enum_type->enumvalue(0).number();
321     }
322   } else {
323     // We don't need to check whether the value is actually declared in the
324     // enum because we preserve unknown enum values as well.
325     return ToInt32();
326   }
327   return InvalidArgument(
328       ValueAsStringOrDefault("Cannot find enum with given value."));
329 }
330 
331 template <typename To>
GenericConvert() const332 StatusOr<To> DataPiece::GenericConvert() const {
333   switch (type_) {
334     case TYPE_INT32:
335       return NumberConvertAndCheck<To, int32>(i32_);
336     case TYPE_INT64:
337       return NumberConvertAndCheck<To, int64>(i64_);
338     case TYPE_UINT32:
339       return NumberConvertAndCheck<To, uint32>(u32_);
340     case TYPE_UINT64:
341       return NumberConvertAndCheck<To, uint64>(u64_);
342     case TYPE_DOUBLE:
343       return NumberConvertAndCheck<To, double>(double_);
344     case TYPE_FLOAT:
345       return NumberConvertAndCheck<To, float>(float_);
346     default:  // TYPE_ENUM, TYPE_STRING, TYPE_CORD, TYPE_BOOL
347       return InvalidArgument(ValueAsStringOrDefault(
348           "Wrong type. Bool, Enum, String and Cord not supported in "
349           "GenericConvert."));
350   }
351 }
352 
353 template <typename To>
StringToNumber(bool (* func)(StringPiece,To *)) const354 StatusOr<To> DataPiece::StringToNumber(bool (*func)(StringPiece,
355                                                     To*)) const {
356   if (str_.size() > 0 && (str_[0] == ' ' || str_[str_.size() - 1] == ' ')) {
357     return InvalidArgument(StrCat("\"", str_, "\""));
358   }
359   To result;
360   if (func(str_, &result)) return result;
361   return InvalidArgument(StrCat("\"", std::string(str_), "\""));
362 }
363 
DecodeBase64(StringPiece src,std::string * dest) const364 bool DataPiece::DecodeBase64(StringPiece src, std::string* dest) const {
365   // Try web-safe decode first, if it fails, try the non-web-safe decode.
366   if (WebSafeBase64Unescape(src, dest)) {
367     if (use_strict_base64_decoding_) {
368       // In strict mode, check if the escaped version gives us the same value as
369       // unescaped.
370       std::string encoded;
371       // WebSafeBase64Escape does no padding by default.
372       WebSafeBase64Escape(*dest, &encoded);
373       // Remove trailing padding '=' characters before comparison.
374       StringPiece src_no_padding = StringPiece(src).substr(
375           0, HasSuffixString(src, "=") ? src.find_last_not_of('=') + 1
376                                       : src.length());
377       return encoded == src_no_padding;
378     }
379     return true;
380   }
381 
382   if (Base64Unescape(src, dest)) {
383     if (use_strict_base64_decoding_) {
384       std::string encoded;
385       Base64Escape(
386           reinterpret_cast<const unsigned char*>(dest->data()), dest->length(),
387           &encoded, false);
388       StringPiece src_no_padding = StringPiece(src).substr(
389           0, HasSuffixString(src, "=") ? src.find_last_not_of('=') + 1
390                                       : src.length());
391       return encoded == src_no_padding;
392     }
393     return true;
394   }
395 
396   return false;
397 }
398 
InternalCopy(const DataPiece & other)399 void DataPiece::InternalCopy(const DataPiece& other) {
400   type_ = other.type_;
401   use_strict_base64_decoding_ = other.use_strict_base64_decoding_;
402   switch (type_) {
403     case TYPE_INT32:
404     case TYPE_INT64:
405     case TYPE_UINT32:
406     case TYPE_UINT64:
407     case TYPE_DOUBLE:
408     case TYPE_FLOAT:
409     case TYPE_BOOL:
410     case TYPE_ENUM:
411     case TYPE_NULL:
412     case TYPE_BYTES:
413     case TYPE_STRING: {
414       str_ = other.str_;
415       break;
416     }
417   }
418 }
419 
420 }  // namespace converter
421 }  // namespace util
422 }  // namespace protobuf
423 }  // namespace google
424