1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 // Defines classes for field comparison. 9 10 #ifndef GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__ 11 #define GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__ 12 13 #include <cstdint> 14 #include <string> 15 #include <vector> 16 17 #include "google/protobuf/stubs/common.h" 18 #include "absl/container/flat_hash_map.h" 19 #include "google/protobuf/port.h" 20 21 // Must be included last. 22 #include "google/protobuf/port_def.inc" 23 24 namespace google { 25 namespace protobuf { 26 27 class Message; 28 class EnumValueDescriptor; 29 class FieldDescriptor; 30 31 namespace util { 32 33 class FieldContext; 34 class MessageDifferencer; 35 36 // Base class specifying the interface for comparing protocol buffer fields. 37 // Regular users should consider using or subclassing DefaultFieldComparator 38 // rather than this interface. 39 // Currently, this does not support comparing unknown fields. 40 class PROTOBUF_EXPORT FieldComparator { 41 public: 42 FieldComparator(); 43 FieldComparator(const FieldComparator&) = delete; 44 FieldComparator& operator=(const FieldComparator&) = delete; 45 virtual ~FieldComparator(); 46 47 enum ComparisonResult { 48 SAME, // Compared fields are equal. In case of comparing submessages, 49 // user should not recursively compare their contents. 50 DIFFERENT, // Compared fields are different. In case of comparing 51 // submessages, user should not recursively compare their 52 // contents. 53 RECURSE, // Compared submessages need to be compared recursively. 54 // FieldComparator does not specify the semantics of recursive 55 // comparison. This value should not be returned for simple 56 // values. 57 }; 58 59 // Compares the values of a field in two protocol buffer messages. 60 // Returns SAME or DIFFERENT for simple values, and SAME, DIFFERENT or RECURSE 61 // for submessages. Returning RECURSE for fields not being submessages is 62 // illegal. 63 // In case the given FieldDescriptor points to a repeated field, the indices 64 // need to be valid. Otherwise they should be ignored. 65 // 66 // FieldContext contains information about the specific instances of the 67 // fields being compared, versus FieldDescriptor which only contains general 68 // type information about the fields. 69 virtual ComparisonResult Compare(const Message& message_1, 70 const Message& message_2, 71 const FieldDescriptor* field, int index_1, 72 int index_2, 73 const util::FieldContext* field_context) = 0; 74 }; 75 76 // Basic implementation of FieldComparator. Supports three modes of floating 77 // point value comparison: exact, approximate using MathUtil::AlmostEqual 78 // method, and arbitrarily precise using MathUtil::WithinFractionOrMargin. 79 class PROTOBUF_EXPORT SimpleFieldComparator : public FieldComparator { 80 public: 81 enum FloatComparison { 82 EXACT, // Floats and doubles are compared exactly. 83 APPROXIMATE, // Floats and doubles are compared using the 84 // MathUtil::AlmostEqual method or 85 // MathUtil::WithinFractionOrMargin method. 86 // TODO: Introduce third value to differentiate uses of AlmostEqual 87 // and WithinFractionOrMargin. 88 }; 89 90 // Creates new comparator with float comparison set to EXACT. 91 SimpleFieldComparator(); 92 SimpleFieldComparator(const SimpleFieldComparator&) = delete; 93 SimpleFieldComparator& operator=(const SimpleFieldComparator&) = delete; 94 95 ~SimpleFieldComparator() override; 96 set_float_comparison(FloatComparison float_comparison)97 void set_float_comparison(FloatComparison float_comparison) { 98 float_comparison_ = float_comparison; 99 } 100 float_comparison()101 FloatComparison float_comparison() const { return float_comparison_; } 102 103 // Set whether the FieldComparator shall treat floats or doubles that are both 104 // NaN as equal (treat_nan_as_equal = true) or as different 105 // (treat_nan_as_equal = false). Default is treating NaNs always as different. set_treat_nan_as_equal(bool treat_nan_as_equal)106 void set_treat_nan_as_equal(bool treat_nan_as_equal) { 107 treat_nan_as_equal_ = treat_nan_as_equal; 108 } 109 treat_nan_as_equal()110 bool treat_nan_as_equal() const { return treat_nan_as_equal_; } 111 112 // Sets the fraction and margin for the float comparison of a given field. 113 // Uses MathUtil::WithinFractionOrMargin to compare the values. 114 // 115 // REQUIRES: field->cpp_type == FieldDescriptor::CPPTYPE_DOUBLE or 116 // field->cpp_type == FieldDescriptor::CPPTYPE_FLOAT 117 // REQUIRES: float_comparison_ == APPROXIMATE 118 void SetFractionAndMargin(const FieldDescriptor* field, double fraction, 119 double margin); 120 121 // Sets the fraction and margin for the float comparison of all float and 122 // double fields, unless a field has been given a specific setting via 123 // SetFractionAndMargin() above. 124 // Uses MathUtil::WithinFractionOrMargin to compare the values. 125 // 126 // REQUIRES: float_comparison_ == APPROXIMATE 127 void SetDefaultFractionAndMargin(double fraction, double margin); 128 129 protected: 130 // Returns the comparison result for the given field in two messages. 131 // 132 // This function is called directly by DefaultFieldComparator::Compare. 133 // Subclasses can call this function to compare fields they do not need to 134 // handle specially. 135 ComparisonResult SimpleCompare(const Message& message_1, 136 const Message& message_2, 137 const FieldDescriptor* field, int index_1, 138 int index_2, 139 const util::FieldContext* field_context); 140 141 // Compare using the provided message_differencer. For example, a subclass can 142 // use this method to compare some field in a certain way using the same 143 // message_differencer instance and the field context. 144 bool CompareWithDifferencer(MessageDifferencer* differencer, 145 const Message& message1, const Message& message2, 146 const util::FieldContext* field_context); 147 148 // Returns FieldComparator::SAME if boolean_result is true and 149 // FieldComparator::DIFFERENT otherwise. 150 ComparisonResult ResultFromBoolean(bool boolean_result) const; 151 152 private: 153 // Defines the tolerance for floating point comparison (fraction and margin). 154 struct Tolerance { 155 double fraction; 156 double margin; ToleranceTolerance157 Tolerance() : fraction(0.0), margin(0.0) {} ToleranceTolerance158 Tolerance(double f, double m) : fraction(f), margin(m) {} 159 }; 160 161 friend class MessageDifferencer; 162 // The following methods get executed when CompareFields is called for the 163 // basic types (instead of submessages). They return true on success. One 164 // can use ResultFromBoolean() to convert that boolean to a ComparisonResult 165 // value. CompareBool(const FieldDescriptor &,bool value_1,bool value_2)166 bool CompareBool(const FieldDescriptor& /* unused */, bool value_1, 167 bool value_2) { 168 return value_1 == value_2; 169 } 170 171 // Uses CompareDoubleOrFloat, a helper function used by both CompareDouble and 172 // CompareFloat. 173 bool CompareDouble(const FieldDescriptor& field, double value_1, 174 double value_2); 175 176 bool CompareEnum(const FieldDescriptor& field, 177 const EnumValueDescriptor* value_1, 178 const EnumValueDescriptor* value_2); 179 180 // Uses CompareDoubleOrFloat, a helper function used by both CompareDouble and 181 // CompareFloat. 182 bool CompareFloat(const FieldDescriptor& field, float value_1, float value_2); 183 CompareInt32(const FieldDescriptor &,int32_t value_1,int32_t value_2)184 bool CompareInt32(const FieldDescriptor& /* unused */, int32_t value_1, 185 int32_t value_2) { 186 return value_1 == value_2; 187 } 188 CompareInt64(const FieldDescriptor &,int64_t value_1,int64_t value_2)189 bool CompareInt64(const FieldDescriptor& /* unused */, int64_t value_1, 190 int64_t value_2) { 191 return value_1 == value_2; 192 } 193 CompareString(const FieldDescriptor &,const std::string & value_1,const std::string & value_2)194 bool CompareString(const FieldDescriptor& /* unused */, 195 const std::string& value_1, const std::string& value_2) { 196 return value_1 == value_2; 197 } 198 CompareUInt32(const FieldDescriptor &,uint32_t value_1,uint32_t value_2)199 bool CompareUInt32(const FieldDescriptor& /* unused */, uint32_t value_1, 200 uint32_t value_2) { 201 return value_1 == value_2; 202 } 203 CompareUInt64(const FieldDescriptor &,uint64_t value_1,uint64_t value_2)204 bool CompareUInt64(const FieldDescriptor& /* unused */, uint64_t value_1, 205 uint64_t value_2) { 206 return value_1 == value_2; 207 } 208 209 // This function is used by CompareDouble and CompareFloat to avoid code 210 // duplication. There are no checks done against types of the values passed, 211 // but it's likely to fail if passed non-numeric arguments. 212 template <typename T> 213 bool CompareDoubleOrFloat(const FieldDescriptor& field, T value_1, T value_2); 214 215 FloatComparison float_comparison_; 216 217 // If true, floats and doubles that are both NaN are considered to be 218 // equal. Otherwise, two floats or doubles that are NaN are considered to be 219 // different. 220 bool treat_nan_as_equal_; 221 222 // True iff default_tolerance_ has been explicitly set. 223 // 224 // If false, then the default tolerance for floats and doubles is that which 225 // is used by MathUtil::AlmostEquals(). 226 bool has_default_tolerance_; 227 228 // Default float/double tolerance. Only meaningful if 229 // has_default_tolerance_ == true. 230 Tolerance default_tolerance_; 231 232 // Field-specific float/double tolerances, which override any default for 233 // those particular fields. 234 absl::flat_hash_map<const FieldDescriptor*, Tolerance> map_tolerance_; 235 }; 236 237 // Default field comparison: use the basic implementation of FieldComparator. 238 class PROTOBUF_EXPORT DefaultFieldComparator final 239 : public SimpleFieldComparator { 240 public: Compare(const Message & message_1,const Message & message_2,const FieldDescriptor * field,int index_1,int index_2,const util::FieldContext * field_context)241 ComparisonResult Compare(const Message& message_1, const Message& message_2, 242 const FieldDescriptor* field, int index_1, 243 int index_2, 244 const util::FieldContext* field_context) override { 245 return SimpleCompare(message_1, message_2, field, index_1, index_2, 246 field_context); 247 } 248 }; 249 250 } // namespace util 251 } // namespace protobuf 252 } // namespace google 253 254 #include "google/protobuf/port_undef.inc" 255 256 #endif // GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__ 257