|  | #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); | 
|  | } | 
|  | } | 
|  | } |