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 <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 
FieldComparator()47 FieldComparator::FieldComparator() {}
~FieldComparator()48 FieldComparator::~FieldComparator() {}
49 
DefaultFieldComparator()50 DefaultFieldComparator::DefaultFieldComparator()
51     : float_comparison_(EXACT),
52       treat_nan_as_equal_(false),
53       has_default_tolerance_(false) {
54 }
55 
~DefaultFieldComparator()56 DefaultFieldComparator::~DefaultFieldComparator() {}
57 
Compare(const google::protobuf::Message & message_1,const google::protobuf::Message & message_2,const google::protobuf::FieldDescriptor * field,int index_1,int index_2,const google::protobuf::util::FieldContext * field_context)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 
SetDefaultFractionAndMargin(double fraction,double margin)133 void DefaultFieldComparator::SetDefaultFractionAndMargin(double fraction,
134                                                          double margin) {
135   default_tolerance_ = Tolerance(fraction, margin);
136   has_default_tolerance_ = true;
137 }
138 
SetFractionAndMargin(const FieldDescriptor * field,double fraction,double margin)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 
CompareDouble(const FieldDescriptor & field,double value_1,double value_2)149 bool DefaultFieldComparator::CompareDouble(const FieldDescriptor& field,
150                                            double value_1, double value_2) {
151   return CompareDoubleOrFloat(field, value_1, value_2);
152 }
153 
CompareEnum(const FieldDescriptor & field,const EnumValueDescriptor * value_1,const EnumValueDescriptor * value_2)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 
CompareFloat(const FieldDescriptor & field,float value_1,float value_2)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>
CompareDoubleOrFloat(const FieldDescriptor & field,T value_1,T value_2)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 explicitly 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 
ResultFromBoolean(bool boolean_result) const201 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