Brian Silverman | 9c614bc | 2016-02-15 20:20:02 -0500 | [diff] [blame^] | 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 | |
| 41 | namespace google { |
| 42 | namespace protobuf { |
| 43 | namespace util { |
| 44 | namespace converter { |
| 45 | |
| 46 | using google::protobuf::EnumDescriptor; |
| 47 | using google::protobuf::EnumValueDescriptor; |
| 48 | ; |
| 49 | ; |
| 50 | using util::error::Code; |
| 51 | using util::Status; |
| 52 | using util::StatusOr; |
| 53 | |
| 54 | namespace { |
| 55 | |
| 56 | inline Status InvalidArgument(StringPiece value_str) { |
| 57 | return Status(util::error::INVALID_ARGUMENT, value_str); |
| 58 | } |
| 59 | |
| 60 | template <typename To, typename From> |
| 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(::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. |
| 77 | template <typename To, typename From> |
| 78 | StatusOr<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. |
| 87 | template <typename To, typename From> |
| 88 | StatusOr<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. |
| 96 | template <typename To, typename From> |
| 97 | StatusOr<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 | |
| 114 | StatusOr<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 | |
| 126 | StatusOr<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 | |
| 138 | StatusOr<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 | |
| 150 | StatusOr<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 | |
| 162 | StatusOr<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 | |
| 175 | StatusOr<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 | |
| 190 | StatusOr<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 | |
| 202 | StatusOr<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 | |
| 217 | string 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 | |
| 247 | StatusOr<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 | |
| 264 | StatusOr<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 | |
| 292 | template <typename To> |
| 293 | StatusOr<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 | |
| 314 | template <typename To> |
| 315 | StatusOr<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 |