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
41namespace google {
42namespace protobuf {
43namespace util {
44namespace converter {
45
46using google::protobuf::EnumDescriptor;
47using google::protobuf::EnumValueDescriptor;
48;
49;
50;
51using util::error::Code;
52using util::Status;
53using util::StatusOr;
54
55namespace {
56
57inline Status InvalidArgument(StringPiece value_str) {
58  return Status(util::error::INVALID_ARGUMENT, value_str);
59}
60
61template <typename To, typename From>
62StatusOr<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.
78template <typename To, typename From>
79StatusOr<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.
88template <typename To, typename From>
89StatusOr<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.
97template <typename To, typename From>
98StatusOr<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
115StatusOr<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
127StatusOr<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
139StatusOr<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
151StatusOr<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
163StatusOr<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
176StatusOr<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
191StatusOr<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
203StatusOr<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
218string 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
248StatusOr<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
262StatusOr<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
290template <typename To>
291StatusOr<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
312template <typename To>
313StatusOr<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
322bool 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