Home | History | Annotate | Download | only in internal
      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 ;
     51 using util::error::Code;
     52 using util::Status;
     53 using util::StatusOr;
     54 
     55 namespace {
     56 
     57 inline Status InvalidArgument(StringPiece value_str) {
     58   return Status(util::error::INVALID_ARGUMENT, value_str);
     59 }
     60 
     61 template <typename To, typename From>
     62 StatusOr<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.
     78 template <typename To, typename From>
     79 StatusOr<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.
     88 template <typename To, typename From>
     89 StatusOr<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.
     97 template <typename To, typename From>
     98 StatusOr<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 
    115 StatusOr<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 
    127 StatusOr<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 
    139 StatusOr<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 
    151 StatusOr<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 
    163 StatusOr<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 
    176 StatusOr<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 
    191 StatusOr<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 
    203 StatusOr<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 
    218 string 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 
    248 StatusOr<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 
    262 StatusOr<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 
    290 template <typename To>
    291 StatusOr<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 
    312 template <typename To>
    313 StatusOr<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 
    322 bool 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