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