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