Created
January 12, 2021 14:08
-
-
Save mjs3339/167aacdd38bdcd4d388dc1c7d07de991 to your computer and use it in GitHub Desktop.
Uses 64bit to 32bit hash Mapping for primitive, Value, and Reference types
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Reflection; | |
using System.Runtime.Serialization.Formatters.Binary; | |
using System.Security; | |
[Serializable] | |
public class MapComparer<T> : IEqualityComparer<T> | |
{ | |
private static Mapping64BitToHash32Bit _hash; | |
private static Type _type = typeof(T); | |
private static readonly BinaryFormatter _bf = new BinaryFormatter(); | |
private static readonly MemoryStream _ms = new MemoryStream(); | |
private readonly bool _isPrimitive; | |
public MapComparer() | |
{ | |
_isPrimitive = IsPrimitive(); | |
_hash = new Mapping64BitToHash32Bit(); | |
_type = typeof(T); | |
} | |
public bool Equals(T x, T y) | |
{ | |
if (x == null || y == null) | |
return false; | |
if (_isPrimitive) | |
return x.Equals(y); | |
var xb = GetBytesObject(x, _type); | |
var yb = GetBytesObject(y, _type); | |
if (xb.Length != yb.Length) | |
return false; | |
return xb.Compare(yb); | |
} | |
public int GetHashCode(T obj) | |
{ | |
var buf = GetBytesObject(obj, _type); | |
return _hash.ComputeHash(buf).ToInt(); | |
} | |
private static byte[] GetBytesObjectSerial(object value) | |
{ | |
if (!_type.IsSerializable) | |
return GetBytesObjectR(value); | |
_ms.SetLength(0); | |
_bf.Serialize(_ms, value); | |
return _ms.ToArray().SubArray(0, (int) _ms.Length); | |
} | |
[SecurityCritical] | |
private static byte[] GetBytesObjectR(object data) | |
{ | |
var result = new List<byte>(); | |
var type = data.GetType(); | |
IEnumerable<FieldInfo> tFields = type.GetFields(); | |
foreach (var fieldInfo in tFields) | |
{ | |
var value = fieldInfo.GetValue(data); | |
var lt = value.GetType(); | |
var buf = GetBytesObject(value, lt); | |
result.AddRange(buf); | |
} | |
var p = type.GetNestedTypes(); | |
if (p.Length > 0) | |
foreach (var t in p) | |
{ | |
var nFields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).ToArray(); | |
if (nFields.Length > 0) | |
if (!nFields[0].IsStatic) | |
return result.ToArray(); | |
foreach (var fieldInfo in nFields) | |
{ | |
var value = fieldInfo.GetValue(null); | |
var lt = value.GetType(); | |
var buf = GetBytesObject(value, lt); | |
result.AddRange(buf); | |
} | |
} | |
return result.ToArray(); | |
} | |
[SecurityCritical] | |
private static byte[] GetBytesObject(object obj, Type ltype) | |
{ | |
switch (ltype.Name.Trim('[', ']')) | |
{ | |
case "Byte": | |
return !ltype.IsArray ? new[] {(byte) obj} : (byte[]) obj; | |
case "Boolean": | |
return !ltype.IsArray ? ((bool) obj).GetBytes() : ((bool[]) obj).GetBytes(); | |
case "SByte": | |
return !ltype.IsArray ? ((sbyte) obj).GetBytes() : ((sbyte[]) obj).GetBytes(); | |
case "Char": | |
return !ltype.IsArray ? ((char) obj).GetBytes() : ((char[]) obj).GetBytes(); | |
case "Int16": | |
return !ltype.IsArray ? ((short) obj).GetBytes() : ((short[]) obj).GetBytes(); | |
case "UInt16": | |
return !ltype.IsArray ? ((ushort) obj).GetBytes() : ((ushort[]) obj).GetBytes(); | |
case "Int32": | |
return !ltype.IsArray ? ((int) obj).GetBytes() : ((int[]) obj).GetBytes(); | |
case "UInt32": | |
return !ltype.IsArray ? ((uint) obj).GetBytes() : ((uint[]) obj).GetBytes(); | |
case "Int64": | |
return !ltype.IsArray ? ((long) obj).GetBytes() : ((long[]) obj).GetBytes(); | |
case "UInt64": | |
return !ltype.IsArray ? ((ulong) obj).GetBytes() : ((ulong[]) obj).GetBytes(); | |
case "Single": | |
return !ltype.IsArray ? ((float) obj).GetBytes() : ((float[]) obj).GetBytes(); | |
case "Double": | |
return !ltype.IsArray ? ((double) obj).GetBytes() : ((double[]) obj).GetBytes(); | |
case "String": | |
return !ltype.IsArray ? ((string) obj).GetBytes() : ((string[]) obj).GetBytes(); | |
case "Decimal": | |
return !ltype.IsArray ? ((decimal) obj).GetBytes() : ((decimal[]) obj).GetBytes(); | |
case "DateTime": | |
return !ltype.IsArray ? ((DateTime) obj).GetBytes() : ((DateTime[]) obj).GetBytes(); | |
} | |
return GetBytesObjectSerial(obj); | |
} | |
private bool IsPrimitive() | |
{ | |
switch (Type.GetTypeCode(_type)) | |
{ | |
case TypeCode.Boolean: | |
case TypeCode.Char: | |
case TypeCode.SByte: | |
case TypeCode.Byte: | |
case TypeCode.Int16: | |
case TypeCode.UInt16: | |
case TypeCode.Int32: | |
case TypeCode.UInt32: | |
case TypeCode.Single: | |
case TypeCode.String: | |
case TypeCode.Decimal: | |
case TypeCode.DateTime: | |
case TypeCode.Int64: | |
case TypeCode.UInt64: | |
case TypeCode.Double: | |
return true; | |
default: | |
return false; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment