MapComparer.cs

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;
        }
    }
}

Mapping64BitToHash32Bit.cs

Maps a 64-Bit Hash Into a 32-Bit Space

using System.Security.Cryptography;
/// <summary>
///     Pros: ~no Collisions within int bit space ~(2,147,483,647)
///     Cons: Maintains a TinyDictionary(byte[],byte[]), non-deterministic across application or
///     method domains
///     Cannot Transform or reuse.
/// </summary>
public class Mapping64BitToHash32Bit : HashAlgorithm
{
    private readonly FNV1a64                        hasher = new FNV1a64();
    public readonly  TinyDictionary<byte[], byte[]> map    = new TinyDictionary<byte[], byte[]>(101, new ArrayComparer());
    private          byte[]                         h64;
    public override  int                            HashSize => 32;
    public override void Initialize()
    {
    }
    /// <inheritdoc />
    /// <summary>
    ///     Compute the 64 Bit hash value and add it and the original array to the map to create a unique position within the
    ///     TinyDictionary.
    /// </summary>
    protected override void HashCore(byte[] bytes, int ibStart, int cbSize)
    {
        h64 = hasher.ComputeHash(bytes, ibStart, cbSize);
        map.Add(h64, bytes);
    }
    /// <inheritdoc />
    /// <summary>
    ///     Return the unique position within the TinyDictionary as the hash value (index).
    /// </summary>
    protected override byte[] HashFinal()
    {
        HashValue = (byte[]) map.FindKeyIndex(h64).GetBytes().Clone();
        return HashValue;
    }
}

Mapping160BitTo64BitHash.cs

Maps a 160-Bit Hash Into a 64-Bit Space

using System.Security.Cryptography;
/// <summary>
///     Pros: ~no Collisions within long bit space ~(9,223,372,036,854,775,807)
///     Cons: Maintains a TinyDictionary(byte[],byte[]), non-deterministic across application or
///     method domains
///     Cannot Transform or reuse.
/// </summary>
public class Mapping160BitTo64BitHash : HashAlgorithm
{
    public readonly  TinyDictionary<byte[], byte[]> map = new TinyDictionary<byte[], byte[]>(101, new ArrayComparer());
    private          byte[]                         h160;
    private readonly SHA1Managed                    hasher = new SHA1Managed();
    public override  int                            HashSize => 64;
    public override void Initialize()
    {
    }
    /// <inheritdoc />
    /// <summary>
    ///     Compute the 64 Bit hash value and add it and the original array to the map to create a unique position within the
    ///     TinyDictionary.
    /// </summary>
    protected override void HashCore(byte[] bytes, int ibStart, int cbSize)
    {
        h160 = hasher.ComputeHash(bytes, ibStart, cbSize);
        map.Add(h160, bytes);
    }
    /// <inheritdoc />
    /// <summary>
    ///     Return the unique position within the TinyDictionary as the hash value (index).
    /// </summary>
    protected override byte[] HashFinal()
    {
        HashValue = (byte[])map.FindKeyIndex(h160).GetBytes().Clone();
        return HashValue;
    }
}