From 43da7ae328b699d9c6e64ea909e348fac3506f73 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Thu, 28 May 2009 21:45:43 +0100 Subject: Fix equality of messages to include unknown fields --- src/ProtocolBuffers/AbstractMessage.cs | 3 +- src/ProtocolBuffers/Collections/Dictionaries.cs | 24 ++-------- src/ProtocolBuffers/Collections/Enumerables.cs | 63 +++++++++++++++++++++++++ src/ProtocolBuffers/Collections/Lists.cs | 27 +++++++++++ src/ProtocolBuffers/Collections/PopsicleList.cs | 32 ++++++++++++- src/ProtocolBuffers/ProtocolBuffers.csproj | 1 + src/ProtocolBuffers/UnknownField.cs | 23 +++++++++ src/ProtocolBuffers/UnknownFieldSet.cs | 16 ++++++- 8 files changed, 165 insertions(+), 24 deletions(-) create mode 100644 src/ProtocolBuffers/Collections/Enumerables.cs (limited to 'src/ProtocolBuffers') diff --git a/src/ProtocolBuffers/AbstractMessage.cs b/src/ProtocolBuffers/AbstractMessage.cs index 11bb948e..27cd77ba 100644 --- a/src/ProtocolBuffers/AbstractMessage.cs +++ b/src/ProtocolBuffers/AbstractMessage.cs @@ -213,13 +213,14 @@ namespace Google.ProtocolBuffers { if (otherMessage == null || otherMessage.DescriptorForType != DescriptorForType) { return false; } - return Dictionaries.Equals(AllFields, otherMessage.AllFields); + return Dictionaries.Equals(AllFields, otherMessage.AllFields) && UnknownFields.Equals(otherMessage.UnknownFields); } public override int GetHashCode() { int hash = 41; hash = (19 * hash) + DescriptorForType.GetHashCode(); hash = (53 * hash) + Dictionaries.GetHashCode(AllFields); + hash = (29 * hash) + UnknownFields.GetHashCode(); return hash; } } diff --git a/src/ProtocolBuffers/Collections/Dictionaries.cs b/src/ProtocolBuffers/Collections/Dictionaries.cs index 70e9690d..2a580385 100644 --- a/src/ProtocolBuffers/Collections/Dictionaries.cs +++ b/src/ProtocolBuffers/Collections/Dictionaries.cs @@ -36,8 +36,7 @@ using System.Collections.Generic; namespace Google.ProtocolBuffers.Collections { /// - /// Non-generic class with generic methods which proxy to the non-generic methods - /// in the generic class. + /// Utility class for dictionaries. /// public static class Dictionaries { @@ -60,27 +59,12 @@ namespace Google.ProtocolBuffers.Collections { IEnumerable leftEnumerable = leftEntry.Value as IEnumerable; IEnumerable rightEnumerable = rightValue as IEnumerable; if (leftEnumerable == null || rightEnumerable == null) { - if (!object.Equals(leftEntry.Value, rightValue)) { + if (!Equals(leftEntry.Value, rightValue)) { return false; } } else { - IEnumerator leftEnumerator = leftEnumerable.GetEnumerator(); - try { - foreach (object rightObject in rightEnumerable) { - if (!leftEnumerator.MoveNext()) { - return false; - } - if (!object.Equals(leftEnumerator.Current, rightObject)) { - return false; - } - } - if (leftEnumerator.MoveNext()) { - return false; - } - } finally { - if (leftEnumerator is IDisposable) { - ((IDisposable)leftEnumerator).Dispose(); - } + if (!Enumerables.Equals(leftEnumerable, rightEnumerable)) { + return false; } } } diff --git a/src/ProtocolBuffers/Collections/Enumerables.cs b/src/ProtocolBuffers/Collections/Enumerables.cs new file mode 100644 index 00000000..0f77ad9a --- /dev/null +++ b/src/ProtocolBuffers/Collections/Enumerables.cs @@ -0,0 +1,63 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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. +using System; +using System.Collections; + +namespace Google.ProtocolBuffers.Collections { + /// + /// Utility class for IEnumerable (and potentially the generic version in the future). + /// + public static class Enumerables { + public static bool Equals(IEnumerable left, IEnumerable right) { + IEnumerator leftEnumerator = left.GetEnumerator(); + try { + foreach (object rightObject in right) { + if (!leftEnumerator.MoveNext()) { + return false; + } + if (!Equals(leftEnumerator.Current, rightObject)) { + return false; + } + } + if (leftEnumerator.MoveNext()) { + return false; + } + } finally { + IDisposable leftEnumeratorDisposable = leftEnumerator as IDisposable; + if (leftEnumeratorDisposable != null) { + leftEnumeratorDisposable.Dispose(); + } + } + return true; + } + } +} diff --git a/src/ProtocolBuffers/Collections/Lists.cs b/src/ProtocolBuffers/Collections/Lists.cs index 8d3a1d46..899baae5 100644 --- a/src/ProtocolBuffers/Collections/Lists.cs +++ b/src/ProtocolBuffers/Collections/Lists.cs @@ -45,6 +45,33 @@ namespace Google.ProtocolBuffers.Collections { public static IList AsReadOnly(IList list) { return Lists.AsReadOnly(list); } + + public static bool Equals(IList left, IList right) { + if (left == right) { + return true; + } + if (left == null || right == null) { + return false; + } + if (left.Count != right.Count) { + return false; + } + IEqualityComparer comparer = EqualityComparer.Default; + for (int i = 0; i < left.Count; i++) { + if (!comparer.Equals(left[i], right[i])) { + return false; + } + } + return true; + } + + public static int GetHashCode(IList list) { + int hash = 31; + foreach (T element in list) { + hash = hash * 29 + element.GetHashCode(); + } + return hash; + } } /// diff --git a/src/ProtocolBuffers/Collections/PopsicleList.cs b/src/ProtocolBuffers/Collections/PopsicleList.cs index 6434f856..a7d01553 100644 --- a/src/ProtocolBuffers/Collections/PopsicleList.cs +++ b/src/ProtocolBuffers/Collections/PopsicleList.cs @@ -1,6 +1,36 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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. using System; using System.Collections.Generic; -using System.Text; using System.Collections; namespace Google.ProtocolBuffers.Collections { diff --git a/src/ProtocolBuffers/ProtocolBuffers.csproj b/src/ProtocolBuffers/ProtocolBuffers.csproj index 5a1419e1..994ceec6 100644 --- a/src/ProtocolBuffers/ProtocolBuffers.csproj +++ b/src/ProtocolBuffers/ProtocolBuffers.csproj @@ -41,6 +41,7 @@ + diff --git a/src/ProtocolBuffers/UnknownField.cs b/src/ProtocolBuffers/UnknownField.cs index 15ab216b..1873e302 100644 --- a/src/ProtocolBuffers/UnknownField.cs +++ b/src/ProtocolBuffers/UnknownField.cs @@ -113,6 +113,29 @@ namespace Google.ProtocolBuffers { get { return groupList; } } + public override bool Equals(object other) { + if (ReferenceEquals(this, other)) { + return true; + } + UnknownField otherField = other as UnknownField; + return otherField != null + && Lists.Equals(varintList, otherField.varintList) + && Lists.Equals(fixed32List, otherField.fixed32List) + && Lists.Equals(fixed64List, otherField.fixed64List) + && Lists.Equals(lengthDelimitedList, otherField.lengthDelimitedList) + && Lists.Equals(groupList, otherField.groupList); + } + + public override int GetHashCode() { + int hash = 43; + hash = hash * 47 + Lists.GetHashCode(varintList); + hash = hash * 47 + Lists.GetHashCode(fixed32List); + hash = hash * 47 + Lists.GetHashCode(fixed64List); + hash = hash * 47 + Lists.GetHashCode(lengthDelimitedList); + hash = hash * 47 + Lists.GetHashCode(groupList); + return hash; + } + /// /// Constructs a new Builder. /// diff --git a/src/ProtocolBuffers/UnknownFieldSet.cs b/src/ProtocolBuffers/UnknownFieldSet.cs index aa371fad..f8cbce4c 100644 --- a/src/ProtocolBuffers/UnknownFieldSet.cs +++ b/src/ProtocolBuffers/UnknownFieldSet.cs @@ -194,6 +194,18 @@ namespace Google.ProtocolBuffers { } } + public override bool Equals(object other) { + if (ReferenceEquals(this, other)) { + return true; + } + UnknownFieldSet otherSet = other as UnknownFieldSet; + return otherSet != null && Dictionaries.Equals(fields, otherSet.fields); + } + + public override int GetHashCode() { + return Dictionaries.GetHashCode(fields); + } + /// /// Parses an UnknownFieldSet from the given input. /// @@ -236,8 +248,8 @@ namespace Google.ProtocolBuffers { // Optimization: We keep around a builder for the last field that was // modified so that we can efficiently add to it multiple times in a // row (important when parsing an unknown repeated field). - int lastFieldNumber; - UnknownField.Builder lastField; + private int lastFieldNumber; + private UnknownField.Builder lastField; internal Builder() { } -- cgit v1.2.3