Skip to content

Instantly share code, notes, and snippets.

@mjs3339
Created January 12, 2021 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mjs3339/167aacdd38bdcd4d388dc1c7d07de991 to your computer and use it in GitHub Desktop.
Save mjs3339/167aacdd38bdcd4d388dc1c7d07de991 to your computer and use it in GitHub Desktop.
Uses 64bit to 32bit hash Mapping for primitive, Value, and Reference types
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