0be82c0865fd9c138b69fc82d1b36349819a016c
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / Utils / ByValueEqualityComparer.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ByValueEqualityComparer.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 using System;
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.Text;
14 using System.Diagnostics;
15 using System.Linq;
16
17 namespace System.Data.Common.Utils
18 {
19     /// <summary>
20     /// An implementation of IEqualityComparer&lt;object&gt; that compares byte[] instances by value, and
21     /// delegates all other equality comparisons to a specified IEqualityComparer. In the default case,
22     /// this provides by-value comparison for instances of the CLR equivalents of all EDM primitive types.
23     /// </summary>
24     internal sealed class ByValueEqualityComparer : IEqualityComparer<object>
25     {
26         /// <summary>
27         /// Provides by-value comparison for instances of the CLR equivalents of all EDM primitive types.
28         /// </summary>
29         internal static readonly ByValueEqualityComparer Default = new ByValueEqualityComparer();
30         
31         private ByValueEqualityComparer()
32         {
33         }
34
35         public new bool Equals(object x, object y)
36         {
37             if (object.Equals(x, y))
38             {
39                 return true;
40             }
41                         
42             // If x and y are both non-null byte arrays, then perform a by-value comparison
43             // based on length and element values, otherwise defer to the default comparison.
44             //
45             byte[] xBytes = x as byte[];
46             byte[] yBytes = y as byte[];
47             if (xBytes != null && yBytes != null)
48             {
49                 return CompareBinaryValues(xBytes, yBytes);
50             }
51
52             return false;
53         }
54
55         public int GetHashCode(object obj)
56         {
57             if (obj != null)
58             {
59                 byte[] bytes = obj as byte[];
60                 if (bytes != null)
61                 {
62                     return ComputeBinaryHashCode(bytes);
63                 }
64             }
65             else
66             {
67                 return 0;
68             }
69             
70             return obj.GetHashCode();
71         }
72
73         internal static int ComputeBinaryHashCode(byte[] bytes)
74         {
75             Debug.Assert(bytes != null, "Byte array cannot be null");
76             int hashCode = 0;
77             for (int i = 0, n = Math.Min(bytes.Length, 7); i < n; i++)
78             {
79                 hashCode = ((hashCode << 5) ^ bytes[i]);
80             }
81             return hashCode;
82         }
83
84         internal static bool CompareBinaryValues(byte[] first, byte[] second)
85         {
86             Debug.Assert(first != null && second != null, "Arguments cannot be null");
87             
88             if (first.Length != second.Length)
89             {
90                 return false;
91             }
92
93             for (int i = 0; i < first.Length; i++)
94             {
95                 if (first[i] != second[i])
96                 {
97                     return false;
98                 }
99             }
100
101             return true;
102         }
103     }
104
105     /// <summary>
106     /// Extends IComparer support to the (non-IComparable) byte[] type, based on by-value comparison.
107     /// </summary>
108     internal class ByValueComparer : IComparer
109     {
110         internal static readonly IComparer Default = new ByValueComparer(Comparer<object>.Default);
111
112         private readonly IComparer nonByValueComparer;
113         private ByValueComparer(IComparer comparer)
114         {
115             Debug.Assert(comparer != null, "Non-ByValue comparer cannot be null");
116             this.nonByValueComparer = comparer;
117         }
118
119         int IComparer.Compare(object x, object y)
120         {
121             if (object.ReferenceEquals(x, y))
122             {
123                 return 0;
124             }
125
126             
127             //We can convert DBNulls to nulls for the purposes of comparison.
128             Debug.Assert(!((object.ReferenceEquals(x, DBNull.Value)) && (object.ReferenceEquals(y,DBNull.Value))), "object.ReferenceEquals should catch the case when both values are dbnull");
129             if (object.ReferenceEquals(x, DBNull.Value))
130             {
131                 x = null;
132             }
133             if (object.ReferenceEquals(y, DBNull.Value))
134             {
135                 y = null;
136             }
137             
138             if (x != null && y != null)
139             {
140                 byte[] xAsBytes = x as byte[];
141                 byte[] yAsBytes = y as byte[];
142                 if (xAsBytes != null && yAsBytes != null)
143                 {
144                     int result = xAsBytes.Length - yAsBytes.Length;
145                     if (result == 0)
146                     {
147                         int idx = 0;
148                         while (result == 0 && idx < xAsBytes.Length)
149                         {
150                             byte xVal = xAsBytes[idx];
151                             byte yVal = yAsBytes[idx];
152                             if (xVal != yVal)
153                             {
154                                 result = xVal - yVal;
155                             }
156                             idx++;
157                         }
158                     }
159                     return result;
160                 }
161             }
162
163             return this.nonByValueComparer.Compare(x, y);
164         }
165     }
166 }