//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner Microsoft // @backupOwner Microsoft //--------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Linq; namespace System.Data.Common.Utils { /// /// An implementation of IEqualityComparer<object> that compares byte[] instances by value, and /// delegates all other equality comparisons to a specified IEqualityComparer. In the default case, /// this provides by-value comparison for instances of the CLR equivalents of all EDM primitive types. /// internal sealed class ByValueEqualityComparer : IEqualityComparer { /// /// Provides by-value comparison for instances of the CLR equivalents of all EDM primitive types. /// internal static readonly ByValueEqualityComparer Default = new ByValueEqualityComparer(); private ByValueEqualityComparer() { } public new bool Equals(object x, object y) { if (object.Equals(x, y)) { return true; } // If x and y are both non-null byte arrays, then perform a by-value comparison // based on length and element values, otherwise defer to the default comparison. // byte[] xBytes = x as byte[]; byte[] yBytes = y as byte[]; if (xBytes != null && yBytes != null) { return CompareBinaryValues(xBytes, yBytes); } return false; } public int GetHashCode(object obj) { if (obj != null) { byte[] bytes = obj as byte[]; if (bytes != null) { return ComputeBinaryHashCode(bytes); } } else { return 0; } return obj.GetHashCode(); } internal static int ComputeBinaryHashCode(byte[] bytes) { Debug.Assert(bytes != null, "Byte array cannot be null"); int hashCode = 0; for (int i = 0, n = Math.Min(bytes.Length, 7); i < n; i++) { hashCode = ((hashCode << 5) ^ bytes[i]); } return hashCode; } internal static bool CompareBinaryValues(byte[] first, byte[] second) { Debug.Assert(first != null && second != null, "Arguments cannot be null"); if (first.Length != second.Length) { return false; } for (int i = 0; i < first.Length; i++) { if (first[i] != second[i]) { return false; } } return true; } } /// /// Extends IComparer support to the (non-IComparable) byte[] type, based on by-value comparison. /// internal class ByValueComparer : IComparer { internal static readonly IComparer Default = new ByValueComparer(Comparer.Default); private readonly IComparer nonByValueComparer; private ByValueComparer(IComparer comparer) { Debug.Assert(comparer != null, "Non-ByValue comparer cannot be null"); this.nonByValueComparer = comparer; } int IComparer.Compare(object x, object y) { if (object.ReferenceEquals(x, y)) { return 0; } //We can convert DBNulls to nulls for the purposes of comparison. Debug.Assert(!((object.ReferenceEquals(x, DBNull.Value)) && (object.ReferenceEquals(y,DBNull.Value))), "object.ReferenceEquals should catch the case when both values are dbnull"); if (object.ReferenceEquals(x, DBNull.Value)) { x = null; } if (object.ReferenceEquals(y, DBNull.Value)) { y = null; } if (x != null && y != null) { byte[] xAsBytes = x as byte[]; byte[] yAsBytes = y as byte[]; if (xAsBytes != null && yAsBytes != null) { int result = xAsBytes.Length - yAsBytes.Length; if (result == 0) { int idx = 0; while (result == 0 && idx < xAsBytes.Length) { byte xVal = xAsBytes[idx]; byte yVal = yAsBytes[idx]; if (xVal != yVal) { result = xVal - yVal; } idx++; } } return result; } } return this.nonByValueComparer.Compare(x, y); } } }