blob: b557429fc1f4b66c8a3ef04a173b53debab56c1b [file] [log] [blame]
Brian Silverman9c614bc2016-02-15 20:20:02 -05001// 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;
50using util::error::Code;
51using util::Status;
52using util::StatusOr;
53
54namespace {
55
56inline Status InvalidArgument(StringPiece value_str) {
57 return Status(util::error::INVALID_ARGUMENT, value_str);
58}
59
60template <typename To, typename From>
61StatusOr<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(::google::protobuf::internal::is_integral<From>::value
67 ? ValueAsString(before)
68 : ::google::protobuf::internal::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.
77template <typename To, typename From>
78StatusOr<To> NumberConvertAndCheck(From before) {
79 if (::google::protobuf::internal::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.
87template <typename To, typename From>
88StatusOr<To> FloatingPointToIntConvertAndCheck(From before) {
89 if (::google::protobuf::internal::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.
96template <typename To, typename From>
97StatusOr<To> FloatingPointConvertAndCheck(From before) {
98 if (MathLimits<From>::IsNaN(before)) {
99 return std::numeric_limits<To>::quiet_NaN();
100 }
101
102 To after = static_cast<To>(before);
103 if (MathUtil::AlmostEquals<To>(after, before)) {
104 return after;
105 } else {
106 return InvalidArgument(::google::protobuf::internal::is_same<From, double>::value
107 ? DoubleAsString(before)
108 : FloatAsString(before));
109 }
110}
111
112} // namespace
113
114StatusOr<int32> DataPiece::ToInt32() const {
115 if (type_ == TYPE_STRING) return StringToNumber<int32>(safe_strto32);
116
117 if (type_ == TYPE_DOUBLE)
118 return FloatingPointToIntConvertAndCheck<int32, double>(double_);
119
120 if (type_ == TYPE_FLOAT)
121 return FloatingPointToIntConvertAndCheck<int32, float>(float_);
122
123 return GenericConvert<int32>();
124}
125
126StatusOr<uint32> DataPiece::ToUint32() const {
127 if (type_ == TYPE_STRING) return StringToNumber<uint32>(safe_strtou32);
128
129 if (type_ == TYPE_DOUBLE)
130 return FloatingPointToIntConvertAndCheck<uint32, double>(double_);
131
132 if (type_ == TYPE_FLOAT)
133 return FloatingPointToIntConvertAndCheck<uint32, float>(float_);
134
135 return GenericConvert<uint32>();
136}
137
138StatusOr<int64> DataPiece::ToInt64() const {
139 if (type_ == TYPE_STRING) return StringToNumber<int64>(safe_strto64);
140
141 if (type_ == TYPE_DOUBLE)
142 return FloatingPointToIntConvertAndCheck<int64, double>(double_);
143
144 if (type_ == TYPE_FLOAT)
145 return FloatingPointToIntConvertAndCheck<int64, float>(float_);
146
147 return GenericConvert<int64>();
148}
149
150StatusOr<uint64> DataPiece::ToUint64() const {
151 if (type_ == TYPE_STRING) return StringToNumber<uint64>(safe_strtou64);
152
153 if (type_ == TYPE_DOUBLE)
154 return FloatingPointToIntConvertAndCheck<uint64, double>(double_);
155
156 if (type_ == TYPE_FLOAT)
157 return FloatingPointToIntConvertAndCheck<uint64, float>(float_);
158
159 return GenericConvert<uint64>();
160}
161
162StatusOr<double> DataPiece::ToDouble() const {
163 if (type_ == TYPE_FLOAT) {
164 return FloatingPointConvertAndCheck<double, float>(float_);
165 }
166 if (type_ == TYPE_STRING) {
167 if (str_ == "Infinity") return std::numeric_limits<double>::infinity();
168 if (str_ == "-Infinity") return -std::numeric_limits<double>::infinity();
169 if (str_ == "NaN") return std::numeric_limits<double>::quiet_NaN();
170 return StringToNumber<double>(safe_strtod);
171 }
172 return GenericConvert<double>();
173}
174
175StatusOr<float> DataPiece::ToFloat() const {
176 if (type_ == TYPE_DOUBLE) {
177 return FloatingPointConvertAndCheck<float, double>(double_);
178 }
179 if (type_ == TYPE_STRING) {
180 if (str_ == "Infinity") return std::numeric_limits<float>::infinity();
181 if (str_ == "-Infinity") return -std::numeric_limits<float>::infinity();
182 if (str_ == "NaN") return std::numeric_limits<float>::quiet_NaN();
183 // SafeStrToFloat() is used instead of safe_strtof() because the later
184 // does not fail on inputs like SimpleDtoa(DBL_MAX).
185 return StringToNumber<float>(SafeStrToFloat);
186 }
187 return GenericConvert<float>();
188}
189
190StatusOr<bool> DataPiece::ToBool() const {
191 switch (type_) {
192 case TYPE_BOOL:
193 return bool_;
194 case TYPE_STRING:
195 return StringToNumber<bool>(safe_strtob);
196 default:
197 return InvalidArgument(
198 ValueAsStringOrDefault("Wrong type. Cannot convert to Bool."));
199 }
200}
201
202StatusOr<string> DataPiece::ToString() const {
203 switch (type_) {
204 case TYPE_STRING:
205 return str_.ToString();
206 case TYPE_BYTES: {
207 string base64;
208 Base64Escape(str_, &base64);
209 return base64;
210 }
211 default:
212 return InvalidArgument(
213 ValueAsStringOrDefault("Cannot convert to string."));
214 }
215}
216
217string DataPiece::ValueAsStringOrDefault(StringPiece default_string) const {
218 switch (type_) {
219 case TYPE_INT32:
220 return SimpleItoa(i32_);
221 case TYPE_INT64:
222 return SimpleItoa(i64_);
223 case TYPE_UINT32:
224 return SimpleItoa(u32_);
225 case TYPE_UINT64:
226 return SimpleItoa(u64_);
227 case TYPE_DOUBLE:
228 return DoubleAsString(double_);
229 case TYPE_FLOAT:
230 return FloatAsString(float_);
231 case TYPE_BOOL:
232 return SimpleBtoa(bool_);
233 case TYPE_STRING:
234 return StrCat("\"", str_.ToString(), "\"");
235 case TYPE_BYTES: {
236 string base64;
237 WebSafeBase64Escape(str_, &base64);
238 return StrCat("\"", base64, "\"");
239 }
240 case TYPE_NULL:
241 return "null";
242 default:
243 return default_string.ToString();
244 }
245}
246
247StatusOr<string> DataPiece::ToBytes() const {
248 if (type_ == TYPE_BYTES) return str_.ToString();
249 if (type_ == TYPE_STRING) {
250 string decoded;
251 if (!WebSafeBase64Unescape(str_, &decoded)) {
252 if (!Base64Unescape(str_, &decoded)) {
253 return InvalidArgument(
254 ValueAsStringOrDefault("Invalid data in input."));
255 }
256 }
257 return decoded;
258 } else {
259 return InvalidArgument(ValueAsStringOrDefault(
260 "Wrong type. Only String or Bytes can be converted to Bytes."));
261 }
262}
263
264StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type) const {
265 if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE;
266
267 if (type_ == TYPE_STRING) {
268 // First try the given value as a name.
269 string enum_name = str_.ToString();
270 const google::protobuf::EnumValue* value =
271 FindEnumValueByNameOrNull(enum_type, enum_name);
272 if (value != NULL) return value->number();
273 // Next try a normalized name.
274 for (string::iterator it = enum_name.begin(); it != enum_name.end(); ++it) {
275 *it = *it == '-' ? '_' : ascii_toupper(*it);
276 }
277 value = FindEnumValueByNameOrNull(enum_type, enum_name);
278 if (value != NULL) return value->number();
279 } else {
280 StatusOr<int32> value = ToInt32();
281 if (value.ok()) {
282 if (const google::protobuf::EnumValue* enum_value =
283 FindEnumValueByNumberOrNull(enum_type, value.ValueOrDie())) {
284 return enum_value->number();
285 }
286 }
287 }
288 return InvalidArgument(
289 ValueAsStringOrDefault("Cannot find enum with given value."));
290}
291
292template <typename To>
293StatusOr<To> DataPiece::GenericConvert() const {
294 switch (type_) {
295 case TYPE_INT32:
296 return NumberConvertAndCheck<To, int32>(i32_);
297 case TYPE_INT64:
298 return NumberConvertAndCheck<To, int64>(i64_);
299 case TYPE_UINT32:
300 return NumberConvertAndCheck<To, uint32>(u32_);
301 case TYPE_UINT64:
302 return NumberConvertAndCheck<To, uint64>(u64_);
303 case TYPE_DOUBLE:
304 return NumberConvertAndCheck<To, double>(double_);
305 case TYPE_FLOAT:
306 return NumberConvertAndCheck<To, float>(float_);
307 default: // TYPE_ENUM, TYPE_STRING, TYPE_CORD, TYPE_BOOL
308 return InvalidArgument(ValueAsStringOrDefault(
309 "Wrong type. Bool, Enum, String and Cord not supported in "
310 "GenericConvert."));
311 }
312}
313
314template <typename To>
315StatusOr<To> DataPiece::StringToNumber(bool (*func)(StringPiece, To*)) const {
316 To result;
317 if (func(str_, &result)) return result;
318 return InvalidArgument(StrCat("\"", str_.ToString(), "\""));
319}
320
321} // namespace converter
322} // namespace util
323} // namespace protobuf
324} // namespace google