Reverse Engineering String.GetHashCode

后端 未结 4 1296
梦毁少年i
梦毁少年i 2021-01-06 08:17

String.GetHashCode\'s behavior is depend on the program architecture. So it will return one value in x86 and one value on x64. I have a test application which must run in x8

4条回答
  •  抹茶落季
    2021-01-06 09:11

    The following exactly reproduces the default String hash codes on .NET 4.7 (and probably earlier). This is the hash code given by:

    • Default on a String instance: "abc".GetHashCode()
    • StringComparer.Ordinal.GetHashCode("abc")
    • Various String methods that take StringComparison.Ordinal enumeration.
    • System.Globalization.CompareInfo.GetStringComparer(CompareOptions.Ordinal)

    Testing on release builds with full JIT optimization, these versions modestly outperform the built-in .NET code, and have also been heavily unit-tested for exact equivalence with .NET behavior. Notice there are separate versions for x86 versus x64. Your program should generally include both; below the respective code listings is a calling harness which selects the appropriate version at runtime.

    x86   -   (.NET running in 32-bit mode)

    static unsafe int GetHashCode_x86_NET(int* p, int c)
    {
        int h1, h2 = h1 = 0x15051505;
    
        while (c > 2)
        {
            h1 = ((h1 << 5) + h1 + (h1 >> 27)) ^ *p++;
            h2 = ((h2 << 5) + h2 + (h2 >> 27)) ^ *p++;
            c -= 4;
        }
    
        if (c > 0)
            h1 = ((h1 << 5) + h1 + (h1 >> 27)) ^ *p++;
    
        return h1 + (h2 * 0x5d588b65);
    }
    

    x64   -   (.NET running in 64-bit mode)

    static unsafe int GetHashCode_x64_NET(Char* p)
    {
        int h1, h2 = h1 = 5381;
    
        while (*p != 0)
        {
            h1 = ((h1 << 5) + h1) ^ *p++;
    
            if (*p == 0)
                break;
    
            h2 = ((h2 << 5) + h2) ^ *p++;
        }
        return h1 + (h2 * 0x5d588b65);
    }
    

    Calling harness / extension method for either platform (x86/x64):

    readonly static int _hash_sz = IntPtr.Size == 4 ? 0x2d2816fe : 0x162a16fe;
    
    public static unsafe int GetStringHashCode(this String s)
    {
        /// Note: x64 string hash ignores remainder after embedded '\0'char (unlike x86)
        if (s.Length == 0 || (IntPtr.Size == 8 && s[0] == '\0'))
            return _hash_sz;
    
        fixed (char* p = s)
            return IntPtr.Size == 4 ?
                GetHashCode_x86_NET((int*)p, s.Length) :
                GetHashCode_x64_NET(p);
    }
    

提交回复
热议问题