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(src, 0, src.ends_with("=")
333 ? src.find_last_not_of('=') + 1
334 : src.length());
335 return encoded == src_no_padding;
336 }
337 return true;
338 }
339
340 if (Base64Unescape(src, dest)) {
341 if (use_strict_base64_decoding_) {
342 string encoded;
343 Base64Escape(
344 reinterpret_cast<const unsigned char*>(dest->data()), dest->length(),
345 &encoded, false);
346 StringPiece src_no_padding(src, 0, src.ends_with("=")
347 ? src.find_last_not_of('=') + 1
348 : src.length());
349 return encoded == src_no_padding;
350 }
351 return true;
352 }
353
354 return false;
355 }
356
357 } // namespace converter
358 } // namespace util
359 } // namespace protobuf
360 } // namespace google
361