• 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 #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 
InvalidArgument(StringPiece value_str)57 inline Status InvalidArgument(StringPiece value_str) {
58   return Status(util::error::INVALID_ARGUMENT, value_str);
59 }
60 
61 template <typename To, typename From>
ValidateNumberConversion(To after,From before)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>
NumberConvertAndCheck(From before)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>
FloatingPointToIntConvertAndCheck(From before)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>
FloatingPointConvertAndCheck(From before)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 
ToInt32() const115 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 
ToUint32() const127 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 
ToInt64() const139 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 
ToUint64() const151 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 
ToDouble() const163 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 
ToFloat() const176 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 
ToBool() const191 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 
ToString() const203 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 
ValueAsStringOrDefault(StringPiece default_string) const218 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 
ToBytes() const248 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 
ToEnum(const google::protobuf::Enum * enum_type) const262 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>
GenericConvert() const291 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>
StringToNumber(bool (* func)(StringPiece,To *)) const313 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 
DecodeBase64(StringPiece src,string * dest) const322 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 = StringPiece(src).substr(
333           0, src.ends_with("=") ? src.find_last_not_of('=') + 1 : src.length());
334       return encoded == src_no_padding;
335     }
336     return true;
337   }
338 
339   if (Base64Unescape(src, dest)) {
340     if (use_strict_base64_decoding_) {
341       string encoded;
342       Base64Escape(
343           reinterpret_cast<const unsigned char*>(dest->data()), dest->length(),
344           &encoded, false);
345       StringPiece src_no_padding = StringPiece(src).substr(
346           0, src.ends_with("=") ? src.find_last_not_of('=') + 1 : src.length());
347       return encoded == src_no_padding;
348     }
349     return true;
350   }
351 
352   return false;
353 }
354 
InternalCopy(const DataPiece & other)355 void DataPiece::InternalCopy(const DataPiece& other) {
356   type_ = other.type_;
357   switch (type_) {
358     case TYPE_INT32:
359     case TYPE_INT64:
360     case TYPE_UINT32:
361     case TYPE_UINT64:
362     case TYPE_DOUBLE:
363     case TYPE_FLOAT:
364     case TYPE_BOOL:
365     case TYPE_ENUM:
366     case TYPE_NULL:
367     case TYPE_BYTES:
368     case TYPE_STRING: {
369       str_ = other.str_;
370       break;
371     }
372   }
373 }
374 
375 }  // namespace converter
376 }  // namespace util
377 }  // namespace protobuf
378 }  // namespace google
379