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