Project

General

Profile

root / branches / compiler / cSharp / ooasCompiler / src / libs / c5 / C5 / Hashers.cs @ 3

1
/*
2
 Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
3
 Permission is hereby granted, free of charge, to any person obtaining a copy
4
 of this software and associated documentation files (the "Software"), to deal
5
 in the Software without restriction, including without limitation the rights
6
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
 copies of the Software, and to permit persons to whom the Software is
8
 furnished to do so, subject to the following conditions:
9
 
10
 The above copyright notice and this permission notice shall be included in
11
 all copies or substantial portions of the Software.
12
 
13
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
 SOFTWARE.
20
*/
21

    
22
using C5;
23
using System;
24
using System.Reflection;
25
using System.Reflection.Emit;
26
using System.Diagnostics;
27
using SCG = System.Collections.Generic;
28

    
29
namespace C5
30
{
31
  /// <summary>
32
  /// Utility class for building default generic equalityComparers.
33
  /// </summary>
34
  /// <typeparam name="T"></typeparam>
35
  public static class EqualityComparer<T>
36
  {
37
    readonly static Type isequenced = typeof(ISequenced<>);
38

    
39
    readonly static Type icollection = typeof(ICollection<>);
40

    
41
    readonly static Type orderedcollectionequalityComparer = typeof(SequencedCollectionEqualityComparer<,>);
42

    
43
    readonly static Type unorderedcollectionequalityComparer = typeof(UnsequencedCollectionEqualityComparer<,>);
44

    
45
    readonly static Type equalityequalityComparer = typeof(EquatableEqualityComparer<>);
46

    
47
    readonly static Type iequalitytype = typeof(IEquatable<T>);
48

    
49
    static SCG.IEqualityComparer<T> cachedDefault = null;
50

    
51
    //TODO: find the right word for initialized+invocation 
52
    /// <summary>
53
    /// A default generic equality comparer for type T. The procedure is as follows:
54
    /// <list>
55
    /// <item>If T is a primitive type (char, sbyte, byte, short, ushort, int, uint, float, double, decimal), 
56
    /// the equalityComparer will be a standard equalityComparer for that type</item>
57
    /// <item>If the actual generic argument T implements the generic interface
58
    /// <see cref="T:C5.ISequenced`1"/> for some value W of its generic parameter T,
59
    /// the equalityComparer will be <see cref="T:C5.SequencedCollectionEqualityComparer`2"/></item>
60
    /// <item>If the actual generic argument T implements 
61
    /// <see cref="T:C5.ICollection`1"/> for some value W of its generic parameter T,
62
    /// the equalityComparer will be <see cref="T:C5.UnsequencedCollectionEqualityComparer`2"/></item>
63
    /// <item>If T is a type implementing <see cref="T:C5.IEquatable`1"/>, the equalityComparer
64
    /// will be <see cref="T:C5.EquatableEqualityComparer`1"/></item>
65
    /// <item>If T is a type not implementing <see cref="T:C5.IEquatable`1"/>, the equalityComparer
66
    /// will be <see cref="T:C5.NaturalEqualityComparer`1"/> </item>
67
    /// </list>   
68
    /// The <see cref="T:C5.IEqualityComparer`1"/> object is constructed when this class is initialised, i.e. 
69
    /// its static constructors called. Thus, the property will be the same object 
70
    /// for the duration of an invocation of the runtime, but a value serialized in 
71
    /// another invocation and deserialized here will not be the same object.
72
    /// </summary>
73
    /// <value></value>
74
    public static SCG.IEqualityComparer<T> Default
75
    {
76
      get
77
      {
78
        if (cachedDefault != null)
79
          return cachedDefault;
80

    
81
        Type t = typeof(T);
82

    
83
        if (t.IsValueType)
84
        {
85
          if (t.Equals(typeof(char)))
86
            return cachedDefault = (SCG.IEqualityComparer<T>)(CharEqualityComparer.Default);
87

    
88
          if (t.Equals(typeof(sbyte)))
89
            return cachedDefault = (SCG.IEqualityComparer<T>)(SByteEqualityComparer.Default);
90

    
91
          if (t.Equals(typeof(byte)))
92
            return cachedDefault = (SCG.IEqualityComparer<T>)(ByteEqualityComparer.Default);
93

    
94
          if (t.Equals(typeof(short)))
95
            return cachedDefault = (SCG.IEqualityComparer<T>)(ShortEqualityComparer.Default);
96

    
97
          if (t.Equals(typeof(ushort)))
98
            return cachedDefault = (SCG.IEqualityComparer<T>)(UShortEqualityComparer.Default);
99
          
100
          if (t.Equals(typeof(int)))
101
            return cachedDefault = (SCG.IEqualityComparer<T>)(IntEqualityComparer.Default);
102

    
103
          if (t.Equals(typeof(uint)))
104
            return cachedDefault = (SCG.IEqualityComparer<T>)(UIntEqualityComparer.Default);
105

    
106
          if (t.Equals(typeof(long)))
107
            return cachedDefault = (SCG.IEqualityComparer<T>)(LongEqualityComparer.Default);
108

    
109
          if (t.Equals(typeof(ulong)))
110
            return cachedDefault = (SCG.IEqualityComparer<T>)(ULongEqualityComparer.Default);
111

    
112
          if (t.Equals(typeof(float)))
113
            return cachedDefault = (SCG.IEqualityComparer<T>)(FloatEqualityComparer.Default);
114
          
115
          if (t.Equals(typeof(double)))
116
            return cachedDefault = (SCG.IEqualityComparer<T>)(DoubleEqualityComparer.Default);
117

    
118
          if (t.Equals(typeof(decimal)))
119
            return cachedDefault = (SCG.IEqualityComparer<T>)(DecimalEqualityComparer.Default);
120
        }
121
        Type[] interfaces = t.GetInterfaces();
122
        if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(isequenced))
123
          return createAndCache(orderedcollectionequalityComparer.MakeGenericType(new Type[] { t, t.GetGenericArguments()[0] }));
124
        foreach (Type ty in interfaces)
125
        {
126
          if (ty.IsGenericType && ty.GetGenericTypeDefinition().Equals(isequenced))
127
            return createAndCache(orderedcollectionequalityComparer.MakeGenericType(new Type[] { t, ty.GetGenericArguments()[0] }));
128
        }
129
        if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(icollection))
130
          return createAndCache(unorderedcollectionequalityComparer.MakeGenericType(new Type[] { t, t.GetGenericArguments()[0] }));
131
        foreach (Type ty in interfaces)
132
        {
133
          if (ty.IsGenericType && ty.GetGenericTypeDefinition().Equals(icollection))
134
            return createAndCache(unorderedcollectionequalityComparer.MakeGenericType(new Type[] { t, ty.GetGenericArguments()[0] }));
135
        }
136
        if (iequalitytype.IsAssignableFrom(t))
137
          return createAndCache(equalityequalityComparer.MakeGenericType(new Type[] { t }));
138
        else
139
          return cachedDefault = NaturalEqualityComparer<T>.Default;
140
      }
141
    }
142
    static SCG.IEqualityComparer<T> createAndCache(Type equalityComparertype)
143
    {
144
      return cachedDefault = (SCG.IEqualityComparer<T>)(equalityComparertype.GetProperty("Default", BindingFlags.Static | BindingFlags.Public).GetValue(null, null));
145
    }
146
  }
147

    
148
  /// <summary>
149
  /// A default item equalityComparer calling through to
150
  /// the GetHashCode and Equals methods inherited from System.Object.
151
  /// </summary>
152
  [Serializable]
153
  public sealed class NaturalEqualityComparer<T> : SCG.IEqualityComparer<T>
154
  {
155
    static NaturalEqualityComparer<T> cached;
156
    NaturalEqualityComparer() { }
157
    /// <summary>
158
    /// 
159
    /// </summary>
160
    /// <value></value>
161
    public static NaturalEqualityComparer<T> Default { get { return cached ?? (cached = new NaturalEqualityComparer<T>()); } }
162
    //TODO: check if null check is reasonable
163
    //Answer: if we have struct C<T> { T t; int i;} and implement GetHashCode as
164
    //the sum of hashcodes, and T may be any type, we cannot make the null check 
165
    //inside the definition of C<T> in a reasonable way.
166
    /// <summary>
167
    /// Get the hash code with respect to this item equalityComparer
168
    /// </summary>
169
    /// <param name="item">The item</param>
170
    /// <returns>The hash code</returns>
171
    [Tested]
172
    public int GetHashCode(T item) { return item == null ? 0 : item.GetHashCode(); }
173

    
174
    /// <summary>
175
    /// Check if two items are equal with respect to this item equalityComparer
176
    /// </summary>
177
    /// <param name="item1">first item</param>
178
    /// <param name="item2">second item</param>
179
    /// <returns>True if equal</returns>
180
    [Tested]
181
    public bool Equals(T item1, T item2)
182
    {
183
      return item1 == null ? item2 == null : item1.Equals(item2);
184
    }
185
  }
186

    
187
  /// <summary>
188
  /// A default equality comparer for a type T that implements System.IEquatable<typeparamref name="T"/>. 
189
  /// 
190
  /// The equality comparer forwards calls to GetHashCode and Equals to the IEquatable methods 
191
  /// on T, so Equals(T) is called, not Equals(object). 
192
  /// This will save boxing abd unboxing if T is a value type
193
  /// and in general saves a runtime type check.
194
  /// </summary>
195
  /// <typeparam name="T"></typeparam>
196
  [Serializable]
197
  public class EquatableEqualityComparer<T> : SCG.IEqualityComparer<T> where T : IEquatable<T>
198
  {
199
    static EquatableEqualityComparer<T> cached = new EquatableEqualityComparer<T>();
200
    EquatableEqualityComparer() { }
201
    /// <summary>
202
    /// 
203
    /// </summary>
204
    /// <value></value>
205
    public static EquatableEqualityComparer<T> Default { 
206
      get { return cached ?? (cached = new EquatableEqualityComparer<T>()); } 
207
    }
208
    /// <summary>
209
    /// 
210
    /// </summary>
211
    /// <param name="item"></param>
212
    /// <returns></returns>
213
    public int GetHashCode(T item) { return item == null ? 0 : item.GetHashCode(); }
214

    
215
    /// <summary>
216
    /// 
217
    /// </summary>
218
    /// <param name="item1"></param>
219
    /// <param name="item2"></param>
220
    /// <returns></returns>
221
    public bool Equals(T item1, T item2) { return item1 == null ? item2 == null : item1.Equals(item2); }
222
  }
223

    
224
  /// <summary>
225
  /// A equalityComparer for a reference type that uses reference equality for equality and the hash code from object as hash code.
226
  /// </summary>
227
  /// <typeparam name="T">The item type. Must be a reference type.</typeparam>
228
  [Serializable]
229
  public class ReferenceEqualityComparer<T> : SCG.IEqualityComparer<T> where T : class
230
  {
231
    static ReferenceEqualityComparer<T> cached;
232
    ReferenceEqualityComparer() { }
233
    /// <summary>
234
    /// 
235
    /// </summary>
236
    /// <value></value>
237
    public static ReferenceEqualityComparer<T> Default { 
238
      get { return cached ?? (cached = new ReferenceEqualityComparer<T>()); } 
239
    }
240
    /// <summary>
241
    /// 
242
    /// </summary>
243
    /// <param name="item"></param>
244
    /// <returns></returns>
245
    public int GetHashCode(T item)
246
    {
247
      return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(item);
248
    }
249

    
250
    /// <summary>
251
    /// 
252
    /// </summary>
253
    /// <param name="i1"></param>
254
    /// <param name="i2"></param>
255
    /// <returns></returns>
256
    public bool Equals(T i1, T i2)
257
    {
258
      return object.ReferenceEquals(i1, i2);
259
    }
260
  }
261

    
262
  /// <summary>
263
  /// An equalityComparer compatible with a given comparer. All hash codes are 0, 
264
  /// meaning that anything based on hash codes will be quite inefficient.
265
  /// <para><b>Note: this will give a new EqualityComparer each time created!</b></para>
266
  /// </summary>
267
  /// <typeparam name="T"></typeparam>
268
  [Serializable]
269
  public class ComparerZeroHashCodeEqualityComparer<T> : SCG.IEqualityComparer<T>
270
  {
271
    SCG.IComparer<T> comparer;
272
    /// <summary>
273
    /// Create a trivial <see cref="T:C5.IEqualityComparer`1"/> compatible with the 
274
    /// <see cref="T:C5.IComparer`1"/> <code>comparer</code>
275
    /// </summary>
276
    /// <param name="comparer"></param>
277
    public ComparerZeroHashCodeEqualityComparer(SCG.IComparer<T> comparer)
278
    {
279
      if (comparer == null)
280
        throw new NullReferenceException("Comparer cannot be null");
281
      this.comparer = comparer;
282
    }
283
    /// <summary>
284
    /// A trivial, inefficient hash fuction. Compatible with any equality relation.
285
    /// </summary>
286
    /// <param name="item"></param>
287
    /// <returns>0</returns>
288
    public int GetHashCode(T item) { return 0; }
289
    /// <summary>
290
    /// Equality of two items as defined by the comparer.
291
    /// </summary>
292
    /// <param name="item1"></param>
293
    /// <param name="item2"></param>
294
    /// <returns></returns>
295
    public bool Equals(T item1, T item2) { return comparer.Compare(item1, item2) == 0; }
296
  }
297

    
298
  /// <summary>
299
  /// Prototype for a sequenced equalityComparer for something (T) that implements ISequenced[W].
300
  /// This will use ISequenced[W] specific implementations of the equality comparer operations.
301
  /// </summary>
302
  /// <typeparam name="T"></typeparam>
303
  /// <typeparam name="W"></typeparam>
304
  [Serializable]
305
  public class SequencedCollectionEqualityComparer<T, W> : SCG.IEqualityComparer<T>
306
      where T : ISequenced<W>
307
  {
308
    static SequencedCollectionEqualityComparer<T, W> cached;
309
    SequencedCollectionEqualityComparer() { }
310
    /// <summary>
311
    /// 
312
    /// </summary>
313
    /// <value></value>
314
    public static SequencedCollectionEqualityComparer<T, W> Default { 
315
      get { return cached ?? (cached = new SequencedCollectionEqualityComparer<T, W>()); } 
316
    }
317
    /// <summary>
318
    /// Get the hash code with respect to this sequenced equalityComparer
319
    /// </summary>
320
    /// <param name="collection">The collection</param>
321
    /// <returns>The hash code</returns>
322
    [Tested]
323
    public int GetHashCode(T collection) { return collection.GetSequencedHashCode(); }
324

    
325
    /// <summary>
326
    /// Check if two items are equal with respect to this sequenced equalityComparer
327
    /// </summary>
328
    /// <param name="collection1">first collection</param>
329
    /// <param name="collection2">second collection</param>
330
    /// <returns>True if equal</returns>
331
    [Tested]
332
    public bool Equals(T collection1, T collection2) { return collection1 == null ? collection2 == null : collection1.SequencedEquals(collection2); }
333
  }
334

    
335
  /// <summary>
336
  /// Prototype for an unsequenced equalityComparer for something (T) that implements ICollection[W]
337
  /// This will use ICollection[W] specific implementations of the equalityComparer operations
338
  /// </summary>
339
  /// <typeparam name="T"></typeparam>
340
  /// <typeparam name="W"></typeparam>
341
  [Serializable]
342
  public class UnsequencedCollectionEqualityComparer<T, W> : SCG.IEqualityComparer<T>
343
      where T : ICollection<W>
344
  {
345
    static UnsequencedCollectionEqualityComparer<T, W> cached;
346
    UnsequencedCollectionEqualityComparer() { }
347
    /// <summary>
348
    /// 
349
    /// </summary>
350
    /// <value></value>
351
    public static UnsequencedCollectionEqualityComparer<T, W> Default { get { return cached ?? (cached = new UnsequencedCollectionEqualityComparer<T, W>()); } }
352
    /// <summary>
353
    /// Get the hash code with respect to this unsequenced equalityComparer
354
    /// </summary>
355
    /// <param name="collection">The collection</param>
356
    /// <returns>The hash code</returns>
357
    [Tested]
358
    public int GetHashCode(T collection) { return collection.GetUnsequencedHashCode(); }
359

    
360

    
361
    /// <summary>
362
    /// Check if two collections are equal with respect to this unsequenced equalityComparer
363
    /// </summary>
364
    /// <param name="collection1">first collection</param>
365
    /// <param name="collection2">second collection</param>
366
    /// <returns>True if equal</returns>
367
    [Tested]
368
    public bool Equals(T collection1, T collection2) { return collection1 == null ? collection2 == null : collection1.UnsequencedEquals(collection2); }
369
  }
370

    
371
}
372

    
373

    
374
#if EXPERIMENTAL
375
namespace C5.EqualityComparerBuilder
376
{
377

    
378
  /// <summary>
379
  /// IEqualityComparer factory class: examines at instatiation time if T is an
380
  /// interface implementing "int GetHashCode()" and "bool Equals(T)".
381
  /// If those are not present, MakeEqualityComparer will return a default equalityComparer,
382
  /// else this class will implement IequalityComparer[T] via Invoke() on the
383
  /// reflected method infos.
384
  /// </summary>
385
  public class ByInvoke<T> : SCG.IEqualityComparer<T>
386
  {
387
    internal static readonly System.Reflection.MethodInfo hinfo, einfo;
388

    
389

    
390
    static ByInvoke()
391
    {
392
      Type t = typeof(T);
393

    
394
      if (!t.IsInterface) return;
395

    
396
      BindingFlags f = BindingFlags.Public | BindingFlags.Instance;
397

    
398
      hinfo = t.GetMethod("GetHashCode", f, null, new Type[0], null);
399
      einfo = t.GetMethod("Equals", f, null, new Type[1] { t }, null);
400
    }
401

    
402

    
403
    private ByInvoke() { }
404

    
405
/// <summary>
406
/// 
407
/// </summary>
408
/// <returns></returns>
409
    public static SCG.IEqualityComparer<T> MakeEqualityComparer()
410
    {
411
      if (hinfo != null && einfo != null)
412
        return new ByInvoke<T>();
413
      else
414
        return NaturalEqualityComparer<T>.Default;
415
    }
416

    
417
/// <summary>
418
/// 
419
/// </summary>
420
/// <param name="item"></param>
421
/// <returns></returns>
422
    public int GetHashCode(T item)
423
    {
424
      return (int)(hinfo.Invoke(item, null));
425
    }
426

    
427
/// <summary>
428
/// 
429
/// </summary>
430
/// <param name="i1"></param>
431
/// <param name="i2"></param>
432
/// <returns></returns>
433
    public bool Equals(T i1, T i2)
434
    {
435
      return (bool)(einfo.Invoke(i1, new object[1] { i2 }));
436
    }
437
  }
438

    
439

    
440

    
441
  /// <summary>
442
  /// Like ByInvoke, but tries to build a equalityComparer by RTCG to
443
  /// avoid the Invoke() overhead. 
444
  /// </summary>
445
  public class ByRTCG
446
  {
447
    private static ModuleBuilder moduleBuilder;
448

    
449
    private static AssemblyBuilder assemblyBuilder;
450

    
451
    /// <summary>
452
    /// 
453
    /// </summary>
454
    /// <param name="hinfo"></param>
455
    /// <param name="einfo"></param>
456
    /// <returns></returns>
457
    public static SCG.IEqualityComparer<T> CreateEqualityComparer<T>(MethodInfo hinfo, MethodInfo einfo)
458
    {
459
      if (moduleBuilder == null)
460
      {
461
        string assmname = "LeFake";
462
        string filename = assmname + ".dll";
463
        AssemblyName assemblyName = new AssemblyName("LeFake");
464
        AppDomain appdomain = AppDomain.CurrentDomain;
465
        AssemblyBuilderAccess acc = AssemblyBuilderAccess.RunAndSave;
466

    
467
        assemblyBuilder = appdomain.DefineDynamicAssembly(assemblyName, acc);
468
        moduleBuilder = assemblyBuilder.DefineDynamicModule(assmname, filename);
469
      }
470

    
471
      Type t = typeof(T);
472
      Type o_t = typeof(object);
473
      Type h_t = typeof(SCG.IEqualityComparer<T>);
474
      Type i_t = typeof(int);
475
      //TODO: protect uid for thread safety!
476
      string name = "C5.Dynamic.EqualityComparer_" + Guid.NewGuid().ToString();
477
      TypeAttributes tatt = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
478
      TypeBuilder tb = moduleBuilder.DefineType(name, tatt, o_t, new Type[1] { h_t });
479
      MethodAttributes matt = MethodAttributes.Public | MethodAttributes.Virtual;
480
      MethodBuilder mb = tb.DefineMethod("GetHashCode", matt, i_t, new Type[1] { t });
481
      ILGenerator ilg = mb.GetILGenerator();
482

    
483
      ilg.Emit(OpCodes.Ldarg_1);
484
      ilg.Emit(OpCodes.Callvirt, hinfo);
485
      ilg.Emit(OpCodes.Ret);
486
      mb = tb.DefineMethod("Equals", matt, typeof(bool), new Type[2] { t, t });
487
      ilg = mb.GetILGenerator();
488
      ilg.Emit(OpCodes.Ldarg_1);
489
      ilg.Emit(OpCodes.Ldarg_2);
490
      ilg.Emit(OpCodes.Callvirt, einfo);
491
      ilg.Emit(OpCodes.Ret);
492

    
493
      Type equalityComparer_t = tb.CreateType();
494
      object equalityComparer = equalityComparer_t.GetConstructor(new Type[0]).Invoke(null);
495

    
496
      return (SCG.IEqualityComparer<T>)equalityComparer;
497
    }
498

    
499
/// <summary>
500
/// 
501
/// </summary>
502
/// <typeparam name="T"></typeparam>
503
/// <returns></returns>
504
    public static SCG.IEqualityComparer<T> build<T>()
505
    {
506
      MethodInfo hinfo = ByInvoke<T>.hinfo, einfo = ByInvoke<T>.einfo;
507

    
508
      if (hinfo != null && einfo != null)
509
        return CreateEqualityComparer<T>(hinfo, einfo);
510
      else
511
        return EqualityComparer<T>.Default;
512
    }
513

    
514
/// <summary>
515
/// 
516
/// </summary>
517
    public void dump()
518
    {
519
      assemblyBuilder.Save("LeFake.dll");
520
    }
521
  }
522
}
523
#endif