• 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 <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 
38 #include <google/protobuf/stubs/strutil.h>
39 #include <google/protobuf/stubs/mathlimits.h>
40 #include <google/protobuf/stubs/mathutil.h>
41 
42 namespace google {
43 namespace protobuf {
44 namespace util {
45 namespace converter {
46 
47 ;
48 ;
49 ;
50 using util::Status;
51 using util::StatusOr;
52 using util::error::Code;
53 
54 namespace {
55 
InvalidArgument(StringPiece value_str)56 inline Status InvalidArgument(StringPiece value_str) {
57   return Status(util::error::INVALID_ARGUMENT, value_str);
58 }
59 
60 template <typename To, typename From>
ValidateNumberConversion(To after,From before)61 StatusOr<To> ValidateNumberConversion(To after, From before) {
62   if (after == before &&
63       MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) {
64     return after;
65   } else {
66     return InvalidArgument(std::is_integral<From>::value
67                                ? ValueAsString(before)
68                                : std::is_same<From, double>::value
69                                      ? DoubleAsString(before)
70                                      : FloatAsString(before));
71   }
72 }
73 
74 // For general conversion between
75 //     int32, int64, uint32, uint64, double and float
76 // except conversion between double and float.
77 template <typename To, typename From>
NumberConvertAndCheck(From before)78 StatusOr<To> NumberConvertAndCheck(From before) {
79   if (std::is_same<From, To>::value) return before;
80 
81   To after = static_cast<To>(before);
82   return ValidateNumberConversion(after, before);
83 }
84 
85 // For conversion to integer types (int32, int64, uint32, uint64) from floating
86 // point types (double, float) only.
87 template <typename To, typename From>
FloatingPointToIntConvertAndCheck(From before)88 StatusOr<To> FloatingPointToIntConvertAndCheck(From before) {
89   if (std::is_same<From, To>::value) return before;
90 
91   To after = static_cast<To>(before);
92   return ValidateNumberConversion(after, before);
93 }
94 
95 // For conversion between double and float only.
FloatToDouble(float before)96 StatusOr<double> FloatToDouble(float before) {
97   // Casting float to double should just work as double has more precision
98   // than float.
99   return static_cast<double>(before);
100 }
101 
DoubleToFloat(double before)102 StatusOr<float> DoubleToFloat(double before) {
103   if (MathLimits<double>::IsNaN(before)) {
104     return std::numeric_limits<float>::quiet_NaN();
105   } else if (!MathLimits<double>::IsFinite(before)) {
106     // Converting a double +inf/-inf to float should just work.
107     return static_cast<float>(before);
108   } else if (before > std::numeric_limits<float>::max() ||
109              before < -std::numeric_limits<float>::max()) {
110     // Double value outside of the range of float.
111     return InvalidArgument(DoubleAsString(before));
112   } else {
113     return static_cast<float>(before);
114   }
115 }
116 
117 }  // namespace
118 
ToInt32() const119 StatusOr<int32> DataPiece::ToInt32() const {
120   if (type_ == TYPE_STRING) return StringToNumber<int32>(safe_strto32);
121 
122   if (type_ == TYPE_DOUBLE)
123     return FloatingPointToIntConvertAndCheck<int32, double>(double_);
124 
125   if (type_ == TYPE_FLOAT)
126     return FloatingPointToIntConvertAndCheck<int32, float>(float_);
127 
128   return GenericConvert<int32>();
129 }
130 
ToUint32() const131 StatusOr<uint32> DataPiece::ToUint32() const {
132   if (type_ == TYPE_STRING)
133     return StringToNumber<uint32>(safe_strtou32);
134 
135   if (type_ == TYPE_DOUBLE)
136     return FloatingPointToIntConvertAndCheck<uint32, double>(double_);
137 
138   if (type_ == TYPE_FLOAT)
139     return FloatingPointToIntConvertAndCheck<uint32, float>(float_);
140 
141   return GenericConvert<uint32>();
142 }
143 
ToInt64() const144 StatusOr<int64> DataPiece::ToInt64() const {
145   if (type_ == TYPE_STRING) return StringToNumber<int64>(safe_strto64);
146 
147   if (type_ == TYPE_DOUBLE)
148     return FloatingPointToIntConvertAndCheck<int64, double>(double_);
149 
150   if (type_ == TYPE_FLOAT)
151     return FloatingPointToIntConvertAndCheck<int64, float>(float_);
152 
153   return GenericConvert<int64>();
154 }
155 
ToUint64() const156 StatusOr<uint64> DataPiece::ToUint64() const {
157   if (type_ == TYPE_STRING)
158     return StringToNumber<uint64>(safe_strtou64);
159 
160   if (type_ == TYPE_DOUBLE)
161     return FloatingPointToIntConvertAndCheck<uint64, double>(double_);
162 
163   if (type_ == TYPE_FLOAT)
164     return FloatingPointToIntConvertAndCheck<uint64, float>(float_);
165 
166   return GenericConvert<uint64>();
167 }
168 
ToDouble() const169 StatusOr<double> DataPiece::ToDouble() const {
170   if (type_ == TYPE_FLOAT) {
171     return FloatToDouble(float_);
172   }
173   if (type_ == TYPE_STRING) {
174     if (str_ == "Infinity") return std::numeric_limits<double>::infinity();
175     if (str_ == "-Infinity") return -std::numeric_limits<double>::infinity();
176     if (str_ == "NaN") return std::numeric_limits<double>::quiet_NaN();
177     StatusOr<double> value = StringToNumber<double>(safe_strtod);
178     if (value.ok() && !MathLimits<double>::IsFinite(value.ValueOrDie())) {
179       // safe_strtod converts out-of-range values to +inf/-inf, but we want
180       // to report them as errors.
181       return InvalidArgument(StrCat("\"", str_, "\""));
182     } else {
183       return value;
184     }
185   }
186   return GenericConvert<double>();
187 }
188 
ToFloat() const189 StatusOr<float> DataPiece::ToFloat() const {
190   if (type_ == TYPE_DOUBLE) {
191     return DoubleToFloat(double_);
192   }
193   if (type_ == TYPE_STRING) {
194     if (str_ == "Infinity") return std::numeric_limits<float>::infinity();
195     if (str_ == "-Infinity") return -std::numeric_limits<float>::infinity();
196     if (str_ == "NaN") return std::numeric_limits<float>::quiet_NaN();
197     // SafeStrToFloat() is used instead of safe_strtof() because the later
198     // does not fail on inputs like SimpleDtoa(DBL_MAX).
199     return StringToNumber<float>(SafeStrToFloat);
200   }
201   return GenericConvert<float>();
202 }
203 
ToBool() const204 StatusOr<bool> DataPiece::ToBool() const {
205   switch (type_) {
206     case TYPE_BOOL:
207       return bool_;
208     case TYPE_STRING:
209       return StringToNumber<bool>(safe_strtob);
210     default:
211       return InvalidArgument(
212           ValueAsStringOrDefault("Wrong type. Cannot convert to Bool."));
213   }
214 }
215 
ToString() const216 StatusOr<std::string> DataPiece::ToString() const {
217   switch (type_) {
218     case TYPE_STRING:
219       return std::string(str_);
220     case TYPE_BYTES: {
221       std::string base64;
222       Base64Escape(str_, &base64);
223       return base64;
224     }
225     default:
226       return InvalidArgument(
227           ValueAsStringOrDefault("Cannot convert to string."));
228   }
229 }
230 
ValueAsStringOrDefault(StringPiece default_string) const231 std::string DataPiece::ValueAsStringOrDefault(
232     StringPiece default_string) const {
233   switch (type_) {
234     case TYPE_INT32:
235       return StrCat(i32_);
236     case TYPE_INT64:
237       return StrCat(i64_);
238     case TYPE_UINT32:
239       return StrCat(u32_);
240     case TYPE_UINT64:
241       return StrCat(u64_);
242     case TYPE_DOUBLE:
243       return DoubleAsString(double_);
244     case TYPE_FLOAT:
245       return FloatAsString(float_);
246     case TYPE_BOOL:
247       return SimpleBtoa(bool_);
248     case TYPE_STRING:
249       return StrCat("\"", str_.ToString(), "\"");
250     case TYPE_BYTES: {
251       std::string base64;
252       WebSafeBase64Escape(str_, &base64);
253       return StrCat("\"", base64, "\"");
254     }
255     case TYPE_NULL:
256       return "null";
257     default:
258       return std::string(default_string);
259   }
260 }
261 
ToBytes() const262 StatusOr<std::string> DataPiece::ToBytes() const {
263   if (type_ == TYPE_BYTES) return str_.ToString();
264   if (type_ == TYPE_STRING) {
265     std::string decoded;
266     if (!DecodeBase64(str_, &decoded)) {
267       return InvalidArgument(ValueAsStringOrDefault("Invalid data in input."));
268     }
269     return decoded;
270   } else {
271     return InvalidArgument(ValueAsStringOrDefault(
272         "Wrong type. Only String or Bytes can be converted to Bytes."));
273   }
274 }
275 
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) const276 StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type,
277                                 bool use_lower_camel_for_enums,
278                                 bool case_insensitive_enum_parsing,
279                                 bool ignore_unknown_enum_values,
280                                 bool* is_unknown_enum_value) const {
281   if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE;
282 
283   if (type_ == TYPE_STRING) {
284     // First try the given value as a name.
285     std::string enum_name = std::string(str_);
286     const google::protobuf::EnumValue* value =
287         FindEnumValueByNameOrNull(enum_type, enum_name);
288     if (value != nullptr) return value->number();
289 
290     // Check if int version of enum is sent as string.
291     StatusOr<int32> int_value = ToInt32();
292     if (int_value.ok()) {
293       if (const google::protobuf::EnumValue* enum_value =
294               FindEnumValueByNumberOrNull(enum_type, int_value.ValueOrDie())) {
295         return enum_value->number();
296       }
297     }
298 
299     // Next try a normalized name.
300     bool should_normalize_enum =
301         case_insensitive_enum_parsing || use_lower_camel_for_enums;
302     if (should_normalize_enum) {
303       for (std::string::iterator it = enum_name.begin(); it != enum_name.end();
304            ++it) {
305         *it = *it == '-' ? '_' : ascii_toupper(*it);
306       }
307       value = FindEnumValueByNameOrNull(enum_type, enum_name);
308       if (value != nullptr) return value->number();
309     }
310 
311     // If use_lower_camel_for_enums is true try with enum name without
312     // underscore. This will also accept camel case names as the enum_name has
313     // been normalized before.
314     if (use_lower_camel_for_enums) {
315       value = FindEnumValueByNameWithoutUnderscoreOrNull(enum_type, enum_name);
316       if (value != nullptr) return value->number();
317     }
318 
319     // If ignore_unknown_enum_values is true an unknown enum value is ignored.
320     if (ignore_unknown_enum_values) {
321       *is_unknown_enum_value = true;
322       return enum_type->enumvalue(0).number();
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("\"", 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(
388           reinterpret_cast<const unsigned char*>(dest->data()), dest->length(),
389           &encoded, false);
390       StringPiece src_no_padding = StringPiece(src).substr(
391           0, HasSuffixString(src, "=") ? src.find_last_not_of('=') + 1
392                                       : src.length());
393       return encoded == src_no_padding;
394     }
395     return true;
396   }
397 
398   return false;
399 }
400 
InternalCopy(const DataPiece & other)401 void DataPiece::InternalCopy(const DataPiece& other) {
402   type_ = other.type_;
403   use_strict_base64_decoding_ = other.use_strict_base64_decoding_;
404   switch (type_) {
405     case TYPE_INT32:
406     case TYPE_INT64:
407     case TYPE_UINT32:
408     case TYPE_UINT64:
409     case TYPE_DOUBLE:
410     case TYPE_FLOAT:
411     case TYPE_BOOL:
412     case TYPE_ENUM:
413     case TYPE_NULL:
414     case TYPE_BYTES:
415     case TYPE_STRING: {
416       str_ = other.str_;
417       break;
418     }
419   }
420 }
421 
422 }  // namespace converter
423 }  // namespace util
424 }  // namespace protobuf
425 }  // namespace google
426