aboutsummaryrefslogtreecommitdiffhomepage
path: root/csharp/src/Google.Protobuf/Collections
diff options
context:
space:
mode:
Diffstat (limited to 'csharp/src/Google.Protobuf/Collections')
-rw-r--r--csharp/src/Google.Protobuf/Collections/Lists.cs89
-rw-r--r--csharp/src/Google.Protobuf/Collections/MapField.cs22
-rw-r--r--csharp/src/Google.Protobuf/Collections/ProtobufEqualityComparers.cs130
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf/Collections/RepeatedField.cs5
4 files changed, 233 insertions, 13 deletions
diff --git a/csharp/src/Google.Protobuf/Collections/Lists.cs b/csharp/src/Google.Protobuf/Collections/Lists.cs
new file mode 100644
index 00000000..860795ce
--- /dev/null
+++ b/csharp/src/Google.Protobuf/Collections/Lists.cs
@@ -0,0 +1,89 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 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.
+#endregion
+
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+namespace Google.Protobuf.Collections
+{
+ /// <summary>
+ /// Utility to compare if two Lists are the same, and the hash code
+ /// of a List.
+ /// </summary>
+ public static class Lists
+ {
+ /// <summary>
+ /// Checks if two lists are equal.
+ /// </summary>
+ public static bool Equals<T>(List<T> left, List<T> right)
+ {
+ if (left == right)
+ {
+ return true;
+ }
+ if (left == null || right == null)
+ {
+ return false;
+ }
+ if (left.Count != right.Count)
+ {
+ return false;
+ }
+ IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
+ for (int i = 0; i < left.Count; i++)
+ {
+ if (!comparer.Equals(left[i], right[i]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Gets the list's hash code.
+ /// </summary>
+ public static int GetHashCode<T>(List<T> list)
+ {
+ if (list == null)
+ {
+ return 0;
+ }
+ int hash = 31;
+ foreach (T element in list)
+ {
+ hash = hash * 29 + element.GetHashCode();
+ }
+ return hash;
+ }
+ }
+} \ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Collections/MapField.cs b/csharp/src/Google.Protobuf/Collections/MapField.cs
index 8dac8e30..dbbcc148 100644
--- a/csharp/src/Google.Protobuf/Collections/MapField.cs
+++ b/csharp/src/Google.Protobuf/Collections/MapField.cs
@@ -71,9 +71,12 @@ namespace Google.Protobuf.Collections
, IReadOnlyDictionary<TKey, TValue>
#endif
{
+ private static readonly EqualityComparer<TValue> ValueEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TValue>();
+ private static readonly EqualityComparer<TKey> KeyEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TKey>();
+
// TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map =
- new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>();
+ new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(KeyEqualityComparer);
private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new LinkedList<KeyValuePair<TKey, TValue>>();
/// <summary>
@@ -131,11 +134,8 @@ namespace Google.Protobuf.Collections
return map.ContainsKey(key);
}
- private bool ContainsValue(TValue value)
- {
- var comparer = EqualityComparer<TValue>.Default;
- return list.Any(pair => comparer.Equals(pair.Value, value));
- }
+ private bool ContainsValue(TValue value) =>
+ list.Any(pair => ValueEqualityComparer.Equals(pair.Value, value));
/// <summary>
/// Removes the entry identified by the given key from the map.
@@ -293,8 +293,7 @@ namespace Google.Protobuf.Collections
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
TValue value;
- return TryGetValue(item.Key, out value)
- && EqualityComparer<TValue>.Default.Equals(item.Value, value);
+ return TryGetValue(item.Key, out value) && ValueEqualityComparer.Equals(item.Value, value);
}
/// <summary>
@@ -363,11 +362,12 @@ namespace Google.Protobuf.Collections
/// </returns>
public override int GetHashCode()
{
- var valueComparer = EqualityComparer<TValue>.Default;
+ var keyComparer = KeyEqualityComparer;
+ var valueComparer = ValueEqualityComparer;
int hash = 0;
foreach (var pair in list)
{
- hash ^= pair.Key.GetHashCode() * 31 + valueComparer.GetHashCode(pair.Value);
+ hash ^= keyComparer.GetHashCode(pair.Key) * 31 + valueComparer.GetHashCode(pair.Value);
}
return hash;
}
@@ -394,7 +394,7 @@ namespace Google.Protobuf.Collections
{
return false;
}
- var valueComparer = EqualityComparer<TValue>.Default;
+ var valueComparer = ValueEqualityComparer;
foreach (var pair in this)
{
TValue value;
diff --git a/csharp/src/Google.Protobuf/Collections/ProtobufEqualityComparers.cs b/csharp/src/Google.Protobuf/Collections/ProtobufEqualityComparers.cs
new file mode 100644
index 00000000..13ef60fc
--- /dev/null
+++ b/csharp/src/Google.Protobuf/Collections/ProtobufEqualityComparers.cs
@@ -0,0 +1,130 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 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.
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace Google.Protobuf.Collections
+{
+ /// <summary>
+ /// Provides a central place to implement equality comparisons, primarily for bitwise float/double equality.
+ /// </summary>
+ public static class ProtobufEqualityComparers
+ {
+ /// <summary>
+ /// Returns an equality comparer for <typeparamref name="T"/> suitable for Protobuf equality comparisons.
+ /// This is usually just the default equality comparer for the type, but floating point numbers are compared
+ /// bitwise.
+ /// </summary>
+ /// <typeparam name="T">The type of equality comparer to return.</typeparam>
+ /// <returns>The equality comparer.</returns>
+ public static EqualityComparer<T> GetEqualityComparer<T>()
+ {
+ return typeof(T) == typeof(double) ? (EqualityComparer<T>) (object) BitwiseDoubleEqualityComparer
+ : typeof(T) == typeof(float) ? (EqualityComparer<T>) (object) BitwiseSingleEqualityComparer
+ : typeof(T) == typeof(double?) ? (EqualityComparer<T>) (object) BitwiseNullableDoubleEqualityComparer
+ : typeof(T) == typeof(float?) ? (EqualityComparer<T>) (object) BitwiseNullableSingleEqualityComparer
+ : EqualityComparer<T>.Default;
+ }
+
+ /// <summary>
+ /// Returns an equality comparer suitable for comparing 64-bit floating point values, by bitwise comparison.
+ /// (NaN values are considered equal, but only when they have the same representation.)
+ /// </summary>
+ public static EqualityComparer<double> BitwiseDoubleEqualityComparer { get; } = new BitwiseDoubleEqualityComparerImpl();
+
+ /// <summary>
+ /// Returns an equality comparer suitable for comparing 32-bit floating point values, by bitwise comparison.
+ /// (NaN values are considered equal, but only when they have the same representation.)
+ /// </summary>
+ public static EqualityComparer<float> BitwiseSingleEqualityComparer { get; } = new BitwiseSingleEqualityComparerImpl();
+
+ /// <summary>
+ /// Returns an equality comparer suitable for comparing nullable 64-bit floating point values, by bitwise comparison.
+ /// (NaN values are considered equal, but only when they have the same representation.)
+ /// </summary>
+ public static EqualityComparer<double?> BitwiseNullableDoubleEqualityComparer { get; } = new BitwiseNullableDoubleEqualityComparerImpl();
+
+ /// <summary>
+ /// Returns an equality comparer suitable for comparing nullable 32-bit floating point values, by bitwise comparison.
+ /// (NaN values are considered equal, but only when they have the same representation.)
+ /// </summary>
+ public static EqualityComparer<float?> BitwiseNullableSingleEqualityComparer { get; } = new BitwiseNullableSingleEqualityComparerImpl();
+
+ private class BitwiseDoubleEqualityComparerImpl : EqualityComparer<double>
+ {
+ public override bool Equals(double x, double y) =>
+ BitConverter.DoubleToInt64Bits(x) == BitConverter.DoubleToInt64Bits(y);
+
+ public override int GetHashCode(double obj) =>
+ BitConverter.DoubleToInt64Bits(obj).GetHashCode();
+ }
+
+ private class BitwiseSingleEqualityComparerImpl : EqualityComparer<float>
+ {
+ // Just promote values to double and use BitConverter.DoubleToInt64Bits,
+ // as there's no BitConverter.SingleToInt32Bits, unfortunately.
+
+ public override bool Equals(float x, float y) =>
+ BitConverter.DoubleToInt64Bits(x) == BitConverter.DoubleToInt64Bits(y);
+
+ public override int GetHashCode(float obj) =>
+ BitConverter.DoubleToInt64Bits(obj).GetHashCode();
+ }
+
+ private class BitwiseNullableDoubleEqualityComparerImpl : EqualityComparer<double?>
+ {
+ public override bool Equals(double? x, double? y) =>
+ x == null && y == null ? true
+ : x == null || y == null ? false
+ : BitwiseDoubleEqualityComparer.Equals(x.Value, y.Value);
+
+ // The hash code for null is just a constant which is at least *unlikely* to be used
+ // elsewhere. (Compared with 0, say.)
+ public override int GetHashCode(double? obj) =>
+ obj == null ? 293864 : BitwiseDoubleEqualityComparer.GetHashCode(obj.Value);
+ }
+
+ private class BitwiseNullableSingleEqualityComparerImpl : EqualityComparer<float?>
+ {
+ public override bool Equals(float? x, float? y) =>
+ x == null && y == null ? true
+ : x == null || y == null ? false
+ : BitwiseSingleEqualityComparer.Equals(x.Value, y.Value);
+
+ // The hash code for null is just a constant which is at least *unlikely* to be used
+ // elsewhere. (Compared with 0, say.)
+ public override int GetHashCode(float? obj) =>
+ obj == null ? 293864 : BitwiseSingleEqualityComparer.GetHashCode(obj.Value);
+ }
+ }
+}
diff --git a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
index 6063ff61..c18b63e2 100755..100644
--- a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
+++ b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
@@ -51,6 +51,7 @@ namespace Google.Protobuf.Collections
, IReadOnlyList<T>
#endif
{
+ private static readonly EqualityComparer<T> EqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<T>();
private static readonly T[] EmptyArray = new T[0];
private const int MinArraySize = 8;
@@ -434,7 +435,7 @@ namespace Google.Protobuf.Collections
{
return false;
}
- EqualityComparer<T> comparer = EqualityComparer<T>.Default;
+ EqualityComparer<T> comparer = EqualityComparer;
for (int i = 0; i < count; i++)
{
if (!comparer.Equals(array[i], other.array[i]))
@@ -454,7 +455,7 @@ namespace Google.Protobuf.Collections
public int IndexOf(T item)
{
ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
- EqualityComparer<T> comparer = EqualityComparer<T>.Default;
+ EqualityComparer<T> comparer = EqualityComparer;
for (int i = 0; i < count; i++)
{
if (comparer.Equals(array[i], item))