Home | History | Annotate | Download | only in util
      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 // Author: ksroka (at) google.com (Krzysztof Sroka)
     32 
     33 #include <google/protobuf/util/field_comparator.h>
     34 
     35 #include <string>
     36 
     37 #include <google/protobuf/descriptor.h>
     38 #include <google/protobuf/message.h>
     39 #include <google/protobuf/stubs/map_util.h>
     40 #include <google/protobuf/stubs/mathlimits.h>
     41 #include <google/protobuf/stubs/mathutil.h>
     42 
     43 namespace google {
     44 namespace protobuf {
     45 namespace util {
     46 
     47 FieldComparator::FieldComparator() {}
     48 FieldComparator::~FieldComparator() {}
     49 
     50 DefaultFieldComparator::DefaultFieldComparator()
     51     : float_comparison_(EXACT),
     52       treat_nan_as_equal_(false),
     53       has_default_tolerance_(false) {
     54 }
     55 
     56 DefaultFieldComparator::~DefaultFieldComparator() {}
     57 
     58 FieldComparator::ComparisonResult DefaultFieldComparator::Compare(
     59       const google::protobuf::Message& message_1,
     60       const google::protobuf::Message& message_2,
     61       const google::protobuf::FieldDescriptor* field,
     62       int index_1, int index_2,
     63       const google::protobuf::util::FieldContext* field_context) {
     64   const Reflection* reflection_1 = message_1.GetReflection();
     65   const Reflection* reflection_2 = message_2.GetReflection();
     66 
     67   switch (field->cpp_type()) {
     68 #define COMPARE_FIELD(METHOD)                                              \
     69     if (field->is_repeated()) {                                            \
     70       return ResultFromBoolean(Compare##METHOD(                            \
     71           *field,                                                          \
     72           reflection_1->GetRepeated##METHOD(message_1, field, index_1),    \
     73           reflection_2->GetRepeated##METHOD(message_2, field, index_2)));  \
     74     } else {                                                               \
     75       return ResultFromBoolean(Compare##METHOD(                            \
     76           *field,                                                          \
     77           reflection_1->Get##METHOD(message_1, field),                     \
     78           reflection_2->Get##METHOD(message_2, field)));                   \
     79     }                                                                      \
     80     break;  // Make sure no fall-through is introduced.
     81 
     82     case FieldDescriptor::CPPTYPE_BOOL:
     83       COMPARE_FIELD(Bool);
     84     case FieldDescriptor::CPPTYPE_DOUBLE:
     85       COMPARE_FIELD(Double);
     86     case FieldDescriptor::CPPTYPE_ENUM:
     87       COMPARE_FIELD(Enum);
     88     case FieldDescriptor::CPPTYPE_FLOAT:
     89       COMPARE_FIELD(Float);
     90     case FieldDescriptor::CPPTYPE_INT32:
     91       COMPARE_FIELD(Int32);
     92     case FieldDescriptor::CPPTYPE_INT64:
     93       COMPARE_FIELD(Int64);
     94     case FieldDescriptor::CPPTYPE_STRING:
     95       if (field->is_repeated()) {
     96         // Allocate scratch strings to store the result if a conversion is
     97         // needed.
     98         string scratch1;
     99         string scratch2;
    100         return ResultFromBoolean(
    101             CompareString(*field, reflection_1->GetRepeatedStringReference(
    102                                       message_1, field, index_1, &scratch1),
    103                           reflection_2->GetRepeatedStringReference(
    104                               message_2, field, index_2, &scratch2)));
    105       } else {
    106         // Allocate scratch strings to store the result if a conversion is
    107         // needed.
    108         string scratch1;
    109         string scratch2;
    110         return ResultFromBoolean(CompareString(
    111             *field,
    112             reflection_1->GetStringReference(message_1, field, &scratch1),
    113             reflection_2->GetStringReference(message_2, field, &scratch2)));
    114       }
    115       break;
    116     case FieldDescriptor::CPPTYPE_UINT32:
    117       COMPARE_FIELD(UInt32);
    118     case FieldDescriptor::CPPTYPE_UINT64:
    119       COMPARE_FIELD(UInt64);
    120 
    121 #undef COMPARE_FIELD
    122 
    123     case FieldDescriptor::CPPTYPE_MESSAGE:
    124       return RECURSE;
    125 
    126     default:
    127       GOOGLE_LOG(FATAL) << "No comparison code for field " << field->full_name()
    128                  << " of CppType = " << field->cpp_type();
    129       return DIFFERENT;
    130   }
    131 }
    132 
    133 void DefaultFieldComparator::SetDefaultFractionAndMargin(double fraction,
    134                                                          double margin) {
    135   default_tolerance_ = Tolerance(fraction, margin);
    136   has_default_tolerance_ = true;
    137 }
    138 
    139 void DefaultFieldComparator::SetFractionAndMargin(const FieldDescriptor* field,
    140                                                   double fraction,
    141                                                   double margin) {
    142   GOOGLE_CHECK(FieldDescriptor::CPPTYPE_FLOAT == field->cpp_type() ||
    143         FieldDescriptor::CPPTYPE_DOUBLE == field->cpp_type())
    144       << "Field has to be float or double type. Field name is: "
    145       << field->full_name();
    146   map_tolerance_[field] = Tolerance(fraction, margin);
    147 }
    148 
    149 bool DefaultFieldComparator::CompareDouble(const FieldDescriptor& field,
    150                                            double value_1, double value_2) {
    151   return CompareDoubleOrFloat(field, value_1, value_2);
    152 }
    153 
    154 bool DefaultFieldComparator::CompareEnum(const FieldDescriptor& field,
    155                                          const EnumValueDescriptor* value_1,
    156                                          const EnumValueDescriptor* value_2) {
    157   return value_1->number() == value_2->number();
    158 }
    159 
    160 bool DefaultFieldComparator::CompareFloat(const FieldDescriptor& field,
    161                                           float value_1, float value_2) {
    162   return CompareDoubleOrFloat(field, value_1, value_2);
    163 }
    164 
    165 template<typename T>
    166 bool DefaultFieldComparator::CompareDoubleOrFloat(const FieldDescriptor& field,
    167                                                   T value_1, T value_2) {
    168   if (value_1 == value_2) {
    169     // Covers +inf and -inf (which are not within margin or fraction of
    170     // themselves), and is a shortcut for finite values.
    171     return true;
    172   } else if (float_comparison_ == EXACT) {
    173     if (treat_nan_as_equal_ &&
    174         MathLimits<T>::IsNaN(value_1) && MathLimits<T>::IsNaN(value_2)) {
    175       return true;
    176     }
    177     return false;
    178   } else {
    179     if (treat_nan_as_equal_ &&
    180         MathLimits<T>::IsNaN(value_1) && MathLimits<T>::IsNaN(value_2)) {
    181       return true;
    182     }
    183     // float_comparison_ == APPROXIMATE covers two use cases.
    184     Tolerance* tolerance = FindOrNull(map_tolerance_, &field);
    185     if (tolerance == NULL && has_default_tolerance_) {
    186       tolerance = &default_tolerance_;
    187     }
    188     if (tolerance == NULL) {
    189       return MathUtil::AlmostEquals(value_1, value_2);
    190     } else {
    191       // Use user-provided fraction and margin. Since they are stored as
    192       // doubles, we explicitely cast them to types of values provided. This
    193       // is very likely to fail if provided values are not numeric.
    194       return MathUtil::WithinFractionOrMargin(
    195           value_1, value_2, static_cast<T>(tolerance->fraction),
    196           static_cast<T>(tolerance->margin));
    197     }
    198   }
    199 }
    200 
    201 FieldComparator::ComparisonResult DefaultFieldComparator::ResultFromBoolean(
    202     bool boolean_result) const {
    203   return boolean_result ? FieldComparator::SAME : FieldComparator::DIFFERENT;
    204 }
    205 
    206 }  // namespace util
    207 }  // namespace protobuf
    208 }  // namespace google
    209