String.GetHashCode的行为取决于程序架构。因此它将在x86中返回一个值,在x64上返回一个值。我有一个必须在x86中运行的测试应用程序,它必须预测必须在x64上运行的应用程序的哈希码输出。
下面是mscorwks中String.GetHashCode实现的反汇编。
public override unsafe int GetHashCode()
{
fixed (char* text1 = ((char*) this))
{
char* chPtr1 = text1;
int num1 = 0x15051505;
int num2 = num1;
int* numPtr1 = (int*) chPtr1;
for (int num3 = this.Length; num3 > 0; num3 -= 4)
{
num1 = (((num1 << 5) + num1) + (num1 >≫ 0x1b)) ^ numPtr1[0];
if (num3 <= 2)
{
break;
}
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr1[1];
numPtr1 += 2;
}
return (num1 + (num2 * 0x5d588b65));
}
}
任何人都可以将此功能移植到安全的实现吗?
答案 0 :(得分:20)
哈希代码不能跨平台重复,甚至不能在同一系统上多次运行同一程序。 你的方向错了。如果你不改变路线,那么你的道路将很艰难,有一天它可能会以泪水结束。
您想要解决的真正的问题是什么?是否有可能编写自己的哈希函数,作为扩展方法或作为包装类的GetHashCode
实现并使用它而不是?
答案 1 :(得分:16)
首先,乔恩是正确的;这是一个傻瓜的差事。我们用来“吃我们自己的dogfood”的框架的内部调试版本每天都会改变哈希算法 ,以防止人们构建依赖于不可靠实现细节的系统 - 甚至是测试系统记录为可随时更改的内容。
我的建议是退后一步并问自己为什么要尝试做一些危险的事情,而不是将系统的仿真记录为不适合仿真的系统。 。这真的是一个要求吗?
其次,StackOverflow是一个技术问答网站,而不是“为我免费工作”网站。如果你一心想做这个危险的事情,你需要有人可以将不安全的代码改写成等效的安全代码,那么我建议你雇用一个可以为你做这件事的人。
答案 2 :(得分:4)
虽然这里给出的所有警告都是有效的,但他们没有回答这个问题。我有一种情况,其中遗憾的是GetHashCode()已被用于生产中的持久值,我别无选择,只能使用默认的.NET 2.0 32位x86(little-endian)算法重新实现。我重新编码没有不安全,如下所示,这似乎是有效的。希望这有助于某人。
// The GetStringHashCode() extension method is equivalent to the Microsoft .NET Framework 2.0
// String.GetHashCode() method executed on 32 bit systems.
public static int GetStringHashCode(this string value)
{
int hash1 = (5381 << 16) + 5381;
int hash2 = hash1;
int len = value.Length;
int intval;
int c0, c1;
int i = 0;
while (len > 0)
{
c0 = (int)value[i];
c1 = (int)value[i + 1];
intval = c0 | (c1 << 16);
hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ intval;
if (len <= 2)
{
break;
}
i += 2;
c0 = (int)value[i];
c1 = len > 3 ? (int)value[i + 1] : 0;
intval = c0 | (c1 << 16);
hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ intval;
len -= 4;
i += 2;
}
return hash1 + (hash2 * 1566083941);
}
答案 3 :(得分:0)
以下内容完全重现 .NET 4.7 上的dbfiddle(可能更早)。这是由下面给出的哈希码:
String
实例的默认值:"abc".GetHashCode()
StringComparer.Ordinal.GetHashCode("abc")
- 采用
String
枚举的各种StringComparison.Ordinal
方法。System.Globalization.CompareInfo.GetStringComparer(CompareOptions.Ordinal)
使用完整的JIT优化测试发布版本,这些版本适度地优于内置的.NET代码,并且还经过严格的单元测试,以确保与.NET
行为完全等效。请注意, x86 与 x64 有单独的版本。您的计划通常应包括两者;在相应的代码清单下面是一个呼叫线束,它在运行时选择适当的版本。
x86 - (.NET以32位模式运行)
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以64位模式运行)
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);
}
为任一平台(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);
}