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 | |
45 | namespace google { |
46 | namespace protobuf { |
47 | namespace util { |
48 | namespace converter { |
49 | |
50 | using util::Status; |
51 | |
52 | namespace { |
53 | |
54 | template <typename To, typename From> |
55 | util::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. |
70 | template <typename To, typename From> |
71 | util::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. |
80 | template <typename To, typename From> |
81 | util::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. |
89 | util::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 | |
95 | util::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 | |
130 | util::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 | |
143 | util::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 | |
156 | util::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 | |
169 | util::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 | |
182 | util::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 | |
202 | util::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 | |
217 | util::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 | |
229 | util::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 | |
244 | std::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 | |
275 | util::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 | |
290 | util::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 | |
349 | template <typename To> |
350 | util::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 | |
371 | template <typename To> |
372 | util::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 | |
383 | bool 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 | |
417 | void 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 | |