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
44namespace google {
45namespace protobuf {
46namespace util {
47
48FieldComparator::FieldComparator() {}
49FieldComparator::~FieldComparator() {}
50
51SimpleFieldComparator::SimpleFieldComparator()
52 : float_comparison_(EXACT),
53 treat_nan_as_equal_(false),
54 has_default_tolerance_(false) {}
55
56SimpleFieldComparator::~SimpleFieldComparator() {}
57
58FieldComparator::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 boolean_result: CompareString(*field,
98 value_1: reflection_1->GetRepeatedStringReference(
99 message: message_1, field, index: index_1, scratch: &scratch1),
100 value_2: reflection_2->GetRepeatedStringReference(
101 message: message_2, field, index: index_2, scratch: &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(boolean_result: CompareString(
108 *field,
109 value_1: reflection_1->GetStringReference(message: message_1, field, scratch: &scratch1),
110 value_2: reflection_2->GetStringReference(message: message_2, field, scratch: &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
130bool SimpleFieldComparator::CompareWithDifferencer(
131 MessageDifferencer* differencer, const Message& message1,
132 const Message& message2, const util::FieldContext* field_context) {
133 return differencer->Compare(message1, message2,
134 parent_fields: field_context->parent_fields());
135}
136
137void SimpleFieldComparator::SetDefaultFractionAndMargin(double fraction,
138 double margin) {
139 default_tolerance_ = Tolerance(fraction, margin);
140 has_default_tolerance_ = true;
141}
142
143void 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
153bool SimpleFieldComparator::CompareDouble(const FieldDescriptor& field,
154 double value_1, double value_2) {
155 return CompareDoubleOrFloat(field, value_1, value_2);
156}
157
158bool 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
164bool SimpleFieldComparator::CompareFloat(const FieldDescriptor& field,
165 float value_1, float value_2) {
166 return CompareDoubleOrFloat(field, value_1, value_2);
167}
168
169template <typename T>
170bool 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(collection&: map_tolerance_, key: &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
203FieldComparator::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