blob: 60b8b8a5ab85840ce2389dfce354b4b66f9c3bcd [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: ksroka@google.com (Krzysztof Sroka)
#include <google/protobuf/util/field_comparator.h>
#include <string>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include <google/protobuf/stubs/map_util.h>
#include <google/protobuf/stubs/mathlimits.h>
#include <google/protobuf/stubs/mathutil.h>
namespace google {
namespace protobuf {
namespace util {
FieldComparator::FieldComparator() {}
FieldComparator::~FieldComparator() {}
DefaultFieldComparator::DefaultFieldComparator()
: float_comparison_(EXACT),
treat_nan_as_equal_(false),
has_default_tolerance_(false) {
}
DefaultFieldComparator::~DefaultFieldComparator() {}
FieldComparator::ComparisonResult DefaultFieldComparator::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) {
const Reflection* reflection_1 = message_1.GetReflection();
const Reflection* reflection_2 = message_2.GetReflection();
switch (field->cpp_type()) {
#define COMPARE_FIELD(METHOD) \
if (field->is_repeated()) { \
return ResultFromBoolean(Compare##METHOD( \
*field, \
reflection_1->GetRepeated##METHOD(message_1, field, index_1), \
reflection_2->GetRepeated##METHOD(message_2, field, index_2))); \
} else { \
return ResultFromBoolean(Compare##METHOD( \
*field, \
reflection_1->Get##METHOD(message_1, field), \
reflection_2->Get##METHOD(message_2, field))); \
} \
break; // Make sure no fall-through is introduced.
case FieldDescriptor::CPPTYPE_BOOL:
COMPARE_FIELD(Bool);
case FieldDescriptor::CPPTYPE_DOUBLE:
COMPARE_FIELD(Double);
case FieldDescriptor::CPPTYPE_ENUM:
COMPARE_FIELD(Enum);
case FieldDescriptor::CPPTYPE_FLOAT:
COMPARE_FIELD(Float);
case FieldDescriptor::CPPTYPE_INT32:
COMPARE_FIELD(Int32);
case FieldDescriptor::CPPTYPE_INT64:
COMPARE_FIELD(Int64);
case FieldDescriptor::CPPTYPE_STRING:
if (field->is_repeated()) {
// Allocate scratch strings to store the result if a conversion is
// needed.
string scratch1;
string scratch2;
return ResultFromBoolean(
CompareString(*field, reflection_1->GetRepeatedStringReference(
message_1, field, index_1, &scratch1),
reflection_2->GetRepeatedStringReference(
message_2, field, index_2, &scratch2)));
} else {
// Allocate scratch strings to store the result if a conversion is
// needed.
string scratch1;
string scratch2;
return ResultFromBoolean(CompareString(
*field,
reflection_1->GetStringReference(message_1, field, &scratch1),
reflection_2->GetStringReference(message_2, field, &scratch2)));
}
break;
case FieldDescriptor::CPPTYPE_UINT32:
COMPARE_FIELD(UInt32);
case FieldDescriptor::CPPTYPE_UINT64:
COMPARE_FIELD(UInt64);
#undef COMPARE_FIELD
case FieldDescriptor::CPPTYPE_MESSAGE:
return RECURSE;
default:
GOOGLE_LOG(FATAL) << "No comparison code for field " << field->full_name()
<< " of CppType = " << field->cpp_type();
return DIFFERENT;
}
}
void DefaultFieldComparator::SetDefaultFractionAndMargin(double fraction,
double margin) {
default_tolerance_ = Tolerance(fraction, margin);
has_default_tolerance_ = true;
}
void DefaultFieldComparator::SetFractionAndMargin(const FieldDescriptor* field,
double fraction,
double margin) {
GOOGLE_CHECK(FieldDescriptor::CPPTYPE_FLOAT == field->cpp_type() ||
FieldDescriptor::CPPTYPE_DOUBLE == field->cpp_type())
<< "Field has to be float or double type. Field name is: "
<< field->full_name();
map_tolerance_[field] = Tolerance(fraction, margin);
}
bool DefaultFieldComparator::CompareDouble(const FieldDescriptor& field,
double value_1, double value_2) {
return CompareDoubleOrFloat(field, value_1, value_2);
}
bool DefaultFieldComparator::CompareEnum(const FieldDescriptor& field,
const EnumValueDescriptor* value_1,
const EnumValueDescriptor* value_2) {
return value_1->number() == value_2->number();
}
bool DefaultFieldComparator::CompareFloat(const FieldDescriptor& field,
float value_1, float value_2) {
return CompareDoubleOrFloat(field, value_1, value_2);
}
template<typename T>
bool DefaultFieldComparator::CompareDoubleOrFloat(const FieldDescriptor& field,
T value_1, T value_2) {
if (value_1 == value_2) {
// Covers +inf and -inf (which are not within margin or fraction of
// themselves), and is a shortcut for finite values.
return true;
} else if (float_comparison_ == EXACT) {
if (treat_nan_as_equal_ &&
MathLimits<T>::IsNaN(value_1) && MathLimits<T>::IsNaN(value_2)) {
return true;
}
return false;
} else {
if (treat_nan_as_equal_ &&
MathLimits<T>::IsNaN(value_1) && MathLimits<T>::IsNaN(value_2)) {
return true;
}
// float_comparison_ == APPROXIMATE covers two use cases.
Tolerance* tolerance = FindOrNull(map_tolerance_, &field);
if (tolerance == NULL && has_default_tolerance_) {
tolerance = &default_tolerance_;
}
if (tolerance == NULL) {
return MathUtil::AlmostEquals(value_1, value_2);
} else {
// Use user-provided fraction and margin. Since they are stored as
// doubles, we explicitely cast them to types of values provided. This
// is very likely to fail if provided values are not numeric.
return MathUtil::WithinFractionOrMargin(
value_1, value_2, static_cast<T>(tolerance->fraction),
static_cast<T>(tolerance->margin));
}
}
}
FieldComparator::ComparisonResult DefaultFieldComparator::ResultFromBoolean(
bool boolean_result) const {
return boolean_result ? FieldComparator::SAME : FieldComparator::DIFFERENT;
}
} // namespace util
} // namespace protobuf
} // namespace google