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 <cstdint>
35#include <limits>
36
37#include <google/protobuf/struct.pb.h>
38#include <google/protobuf/type.pb.h>
39#include <google/protobuf/descriptor.h>
40#include <google/protobuf/stubs/status.h>
41#include <google/protobuf/stubs/strutil.h>
42#include <google/protobuf/util/internal/utility.h>
43#include <google/protobuf/stubs/mathutil.h>
44
45namespace google {
46namespace protobuf {
47namespace util {
48namespace converter {
49
50using util::Status;
51
52namespace {
53
54template <typename To, typename From>
55util::StatusOr<To> ValidateNumberConversion(To after, From before) {
56 if (after == before &&
57 MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) {
58 return after;
59 } else {
60 return util::InvalidArgumentError(
61 message: std::is_integral<From>::value ? ValueAsString(before)
62 : std::is_same<From, double>::value ? DoubleAsString(before)
63 : FloatAsString(before));
64 }
65}
66
67// For general conversion between
68// int32, int64, uint32, uint64, double and float
69// except conversion between double and float.
70template <typename To, typename From>
71util::StatusOr<To> NumberConvertAndCheck(From before) {
72 if (std::is_same<From, To>::value) return before;
73
74 To after = static_cast<To>(before);
75 return ValidateNumberConversion(after, before);
76}
77
78// For conversion to integer types (int32, int64, uint32, uint64) from floating
79// point types (double, float) only.
80template <typename To, typename From>
81util::StatusOr<To> FloatingPointToIntConvertAndCheck(From before) {
82 if (std::is_same<From, To>::value) return before;
83
84 To after = static_cast<To>(before);
85 return ValidateNumberConversion(after, before);
86}
87
88// For conversion between double and float only.
89util::StatusOr<double> FloatToDouble(float before) {
90 // Casting float to double should just work as double has more precision
91 // than float.
92 return static_cast<double>(before);
93}
94
95util::StatusOr<float> DoubleToFloat(double before) {
96 if (std::isnan(x: before)) {
97 return std::numeric_limits<float>::quiet_NaN();
98 } else if (!std::isfinite(x: before)) {
99 // Converting a double +inf/-inf to float should just work.
100 return static_cast<float>(before);
101 } else if (before > std::numeric_limits<float>::max() ||
102 before < -std::numeric_limits<float>::max()) {
103 // Some doubles are larger than the largest float, but after
104 // rounding they will be equal to the largest float.
105 // We can't just attempt the conversion because that has UB if
106 // the value really is out-of-range.
107 // Here we take advantage that 1/2-ing a large floating point
108 // will not lose precision.
109 double half_before = before * 0.5;
110 if (half_before < std::numeric_limits<float>::max() &&
111 half_before > -std::numeric_limits<float>::max()) {
112 const float half_fmax = std::numeric_limits<float>::max() * 0.5f;
113 // If after being cut in half, the value is less than the largest float,
114 // then it's safe to convert it to float. Importantly, this conversion
115 // rounds in the same way that the original does.
116 float half_after = static_cast<float>(half_before);
117 if (half_after <= half_fmax && half_after >= -half_fmax) {
118 return half_after + half_after;
119 }
120 }
121 // Double value outside of the range of float.
122 return util::InvalidArgumentError(message: DoubleAsString(value: before));
123 } else {
124 return static_cast<float>(before);
125 }
126}
127
128} // namespace
129
130util::StatusOr<int32_t> DataPiece::ToInt32() const {
131 if (type_ == TYPE_STRING)
132 return StringToNumber<int32_t>(func: safe_strto32);
133
134 if (type_ == TYPE_DOUBLE)
135 return FloatingPointToIntConvertAndCheck<int32_t, double>(before: double_);
136
137 if (type_ == TYPE_FLOAT)
138 return FloatingPointToIntConvertAndCheck<int32_t, float>(before: float_);
139
140 return GenericConvert<int32_t>();
141}
142
143util::StatusOr<uint32_t> DataPiece::ToUint32() const {
144 if (type_ == TYPE_STRING)
145 return StringToNumber<uint32_t>(func: safe_strtou32);
146
147 if (type_ == TYPE_DOUBLE)
148 return FloatingPointToIntConvertAndCheck<uint32_t, double>(before: double_);
149
150 if (type_ == TYPE_FLOAT)
151 return FloatingPointToIntConvertAndCheck<uint32_t, float>(before: float_);
152
153 return GenericConvert<uint32_t>();
154}
155
156util::StatusOr<int64_t> DataPiece::ToInt64() const {
157 if (type_ == TYPE_STRING)
158 return StringToNumber<int64_t>(func: safe_strto64);
159
160 if (type_ == TYPE_DOUBLE)
161 return FloatingPointToIntConvertAndCheck<int64_t, double>(before: double_);
162
163 if (type_ == TYPE_FLOAT)
164 return FloatingPointToIntConvertAndCheck<int64_t, float>(before: float_);
165
166 return GenericConvert<int64_t>();
167}
168
169util::StatusOr<uint64_t> DataPiece::ToUint64() const {
170 if (type_ == TYPE_STRING)
171 return StringToNumber<uint64_t>(func: safe_strtou64);
172
173 if (type_ == TYPE_DOUBLE)
174 return FloatingPointToIntConvertAndCheck<uint64_t, double>(before: double_);
175
176 if (type_ == TYPE_FLOAT)
177 return FloatingPointToIntConvertAndCheck<uint64_t, float>(before: float_);
178
179 return GenericConvert<uint64_t>();
180}
181
182util::StatusOr<double> DataPiece::ToDouble() const {
183 if (type_ == TYPE_FLOAT) {
184 return FloatToDouble(before: float_);
185 }
186 if (type_ == TYPE_STRING) {
187 if (str_ == "Infinity") return std::numeric_limits<double>::infinity();
188 if (str_ == "-Infinity") return -std::numeric_limits<double>::infinity();
189 if (str_ == "NaN") return std::numeric_limits<double>::quiet_NaN();
190 util::StatusOr<double> value = StringToNumber<double>(func: safe_strtod);
191 if (value.ok() && !std::isfinite(x: value.value())) {
192 // safe_strtod converts out-of-range values to +inf/-inf, but we want
193 // to report them as errors.
194 return util::InvalidArgumentError(message: StrCat(a: "\"", b: str_, c: "\""));
195 } else {
196 return value;
197 }
198 }
199 return GenericConvert<double>();
200}
201
202util::StatusOr<float> DataPiece::ToFloat() const {
203 if (type_ == TYPE_DOUBLE) {
204 return DoubleToFloat(before: double_);
205 }
206 if (type_ == TYPE_STRING) {
207 if (str_ == "Infinity") return std::numeric_limits<float>::infinity();
208 if (str_ == "-Infinity") return -std::numeric_limits<float>::infinity();
209 if (str_ == "NaN") return std::numeric_limits<float>::quiet_NaN();
210 // SafeStrToFloat() is used instead of safe_strtof() because the later
211 // does not fail on inputs like SimpleDtoa(DBL_MAX).
212 return StringToNumber<float>(func: SafeStrToFloat);
213 }
214 return GenericConvert<float>();
215}
216
217util::StatusOr<bool> DataPiece::ToBool() const {
218 switch (type_) {
219 case TYPE_BOOL:
220 return bool_;
221 case TYPE_STRING:
222 return StringToNumber<bool>(func: safe_strtob);
223 default:
224 return util::InvalidArgumentError(
225 message: ValueAsStringOrDefault(default_string: "Wrong type. Cannot convert to Bool."));
226 }
227}
228
229util::StatusOr<std::string> DataPiece::ToString() const {
230 switch (type_) {
231 case TYPE_STRING:
232 return std::string(str_);
233 case TYPE_BYTES: {
234 std::string base64;
235 Base64Escape(src: str_, dest: &base64);
236 return base64;
237 }
238 default:
239 return util::InvalidArgumentError(
240 message: ValueAsStringOrDefault(default_string: "Cannot convert to string."));
241 }
242}
243
244std::string DataPiece::ValueAsStringOrDefault(
245 StringPiece default_string) const {
246 switch (type_) {
247 case TYPE_INT32:
248 return StrCat(a: i32_);
249 case TYPE_INT64:
250 return StrCat(a: i64_);
251 case TYPE_UINT32:
252 return StrCat(a: u32_);
253 case TYPE_UINT64:
254 return StrCat(a: u64_);
255 case TYPE_DOUBLE:
256 return DoubleAsString(value: double_);
257 case TYPE_FLOAT:
258 return FloatAsString(value: float_);
259 case TYPE_BOOL:
260 return SimpleBtoa(value: bool_);
261 case TYPE_STRING:
262 return StrCat(a: "\"", b: str_.ToString(), c: "\"");
263 case TYPE_BYTES: {
264 std::string base64;
265 WebSafeBase64Escape(src: str_, dest: &base64);
266 return StrCat(a: "\"", b: base64, c: "\"");
267 }
268 case TYPE_NULL:
269 return "null";
270 default:
271 return std::string(default_string);
272 }
273}
274
275util::StatusOr<std::string> DataPiece::ToBytes() const {
276 if (type_ == TYPE_BYTES) return str_.ToString();
277 if (type_ == TYPE_STRING) {
278 std::string decoded;
279 if (!DecodeBase64(src: str_, dest: &decoded)) {
280 return util::InvalidArgumentError(
281 message: ValueAsStringOrDefault(default_string: "Invalid data in input."));
282 }
283 return decoded;
284 } else {
285 return util::InvalidArgumentError(message: ValueAsStringOrDefault(
286 default_string: "Wrong type. Only String or Bytes can be converted to Bytes."));
287 }
288}
289
290util::StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type,
291 bool use_lower_camel_for_enums,
292 bool case_insensitive_enum_parsing,
293 bool ignore_unknown_enum_values,
294 bool* is_unknown_enum_value) const {
295 if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE;
296
297 if (type_ == TYPE_STRING) {
298 // First try the given value as a name.
299 std::string enum_name = std::string(str_);
300 const google::protobuf::EnumValue* value =
301 FindEnumValueByNameOrNull(enum_type, enum_name);
302 if (value != nullptr) return value->number();
303
304 // Check if int version of enum is sent as string.
305 util::StatusOr<int32_t> int_value = ToInt32();
306 if (int_value.ok()) {
307 if (const google::protobuf::EnumValue* enum_value =
308 FindEnumValueByNumberOrNull(enum_type, value: int_value.value())) {
309 return enum_value->number();
310 }
311 }
312
313 // Next try a normalized name.
314 bool should_normalize_enum =
315 case_insensitive_enum_parsing || use_lower_camel_for_enums;
316 if (should_normalize_enum) {
317 for (std::string::iterator it = enum_name.begin(); it != enum_name.end();
318 ++it) {
319 *it = *it == '-' ? '_' : ascii_toupper(c: *it);
320 }
321 value = FindEnumValueByNameOrNull(enum_type, enum_name);
322 if (value != nullptr) return value->number();
323 }
324
325 // If use_lower_camel_for_enums is true try with enum name without
326 // underscore. This will also accept camel case names as the enum_name has
327 // been normalized before.
328 if (use_lower_camel_for_enums) {
329 value = FindEnumValueByNameWithoutUnderscoreOrNull(enum_type, enum_name);
330 if (value != nullptr) return value->number();
331 }
332
333 // If ignore_unknown_enum_values is true an unknown enum value is ignored.
334 if (ignore_unknown_enum_values) {
335 *is_unknown_enum_value = true;
336 if (enum_type->enumvalue_size() > 0) {
337 return enum_type->enumvalue(index: 0).number();
338 }
339 }
340 } else {
341 // We don't need to check whether the value is actually declared in the
342 // enum because we preserve unknown enum values as well.
343 return ToInt32();
344 }
345 return util::InvalidArgumentError(
346 message: ValueAsStringOrDefault(default_string: "Cannot find enum with given value."));
347}
348
349template <typename To>
350util::StatusOr<To> DataPiece::GenericConvert() const {
351 switch (type_) {
352 case TYPE_INT32:
353 return NumberConvertAndCheck<To, int32_t>(i32_);
354 case TYPE_INT64:
355 return NumberConvertAndCheck<To, int64_t>(i64_);
356 case TYPE_UINT32:
357 return NumberConvertAndCheck<To, uint32_t>(u32_);
358 case TYPE_UINT64:
359 return NumberConvertAndCheck<To, uint64_t>(u64_);
360 case TYPE_DOUBLE:
361 return NumberConvertAndCheck<To, double>(double_);
362 case TYPE_FLOAT:
363 return NumberConvertAndCheck<To, float>(float_);
364 default: // TYPE_ENUM, TYPE_STRING, TYPE_CORD, TYPE_BOOL
365 return util::InvalidArgumentError(message: ValueAsStringOrDefault(
366 default_string: "Wrong type. Bool, Enum, String and Cord not supported in "
367 "GenericConvert."));
368 }
369}
370
371template <typename To>
372util::StatusOr<To> DataPiece::StringToNumber(bool (*func)(StringPiece,
373 To*)) const {
374 if (str_.size() > 0 && (str_[0] == ' ' || str_[str_.size() - 1] == ' ')) {
375 return util::InvalidArgumentError(message: StrCat(a: "\"", b: str_, c: "\""));
376 }
377 To result;
378 if (func(str_, &result)) return result;
379 return util::InvalidArgumentError(
380 message: StrCat(a: "\"", b: std::string(str_), c: "\""));
381}
382
383bool DataPiece::DecodeBase64(StringPiece src, std::string* dest) const {
384 // Try web-safe decode first, if it fails, try the non-web-safe decode.
385 if (WebSafeBase64Unescape(src, dest)) {
386 if (use_strict_base64_decoding_) {
387 // In strict mode, check if the escaped version gives us the same value as
388 // unescaped.
389 std::string encoded;
390 // WebSafeBase64Escape does no padding by default.
391 WebSafeBase64Escape(src: *dest, dest: &encoded);
392 // Remove trailing padding '=' characters before comparison.
393 StringPiece src_no_padding = StringPiece(src).substr(
394 pos: 0, n: HasSuffixString(str: src, suffix: "=") ? src.find_last_not_of(c: '=') + 1
395 : src.length());
396 return encoded == src_no_padding;
397 }
398 return true;
399 }
400
401 if (Base64Unescape(src, dest)) {
402 if (use_strict_base64_decoding_) {
403 std::string encoded;
404 Base64Escape(src: reinterpret_cast<const unsigned char*>(dest->data()),
405 szsrc: dest->length(), dest: &encoded, do_padding: false);
406 StringPiece src_no_padding = StringPiece(src).substr(
407 pos: 0, n: HasSuffixString(str: src, suffix: "=") ? src.find_last_not_of(c: '=') + 1
408 : src.length());
409 return encoded == src_no_padding;
410 }
411 return true;
412 }
413
414 return false;
415}
416
417void DataPiece::InternalCopy(const DataPiece& other) {
418 type_ = other.type_;
419 use_strict_base64_decoding_ = other.use_strict_base64_decoding_;
420 switch (type_) {
421 case TYPE_INT32:
422 case TYPE_INT64:
423 case TYPE_UINT32:
424 case TYPE_UINT64:
425 case TYPE_DOUBLE:
426 case TYPE_FLOAT:
427 case TYPE_BOOL:
428 case TYPE_ENUM:
429 case TYPE_NULL:
430 case TYPE_BYTES:
431 case TYPE_STRING: {
432 str_ = other.str_;
433 break;
434 }
435 }
436}
437
438} // namespace converter
439} // namespace util
440} // namespace protobuf
441} // namespace google
442