2016-08-03 10:59:49 +00:00
//---------------------------------------------------------------------
// <copyright file="ByValueEqualityComparer.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
2017-08-21 15:34:15 +00:00
// @owner Microsoft
// @backupOwner Microsoft
2016-08-03 10:59:49 +00:00
//---------------------------------------------------------------------
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Text ;
using System.Diagnostics ;
using System.Linq ;
namespace System.Data.Common.Utils
{
/// <summary>
/// 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.
/// </summary>
internal sealed class ByValueEqualityComparer : IEqualityComparer < object >
{
/// <summary>
/// Provides by-value comparison for instances of the CLR equivalents of all EDM primitive types.
/// </summary>
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 ;
}
}
/// <summary>
/// Extends IComparer support to the (non-IComparable) byte[] type, based on by-value comparison.
/// </summary>
internal class ByValueComparer : IComparer
{
internal static readonly IComparer Default = new ByValueComparer ( Comparer < object > . 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 ) ;
}
}
}