我一直在对Mathematica 10.4的32位和64位Windows版本进行反向工程,发现了以下内容:
32位
它使用
Fowler-Noll-Vo哈希函数(FNV-1,乘法在前)作为哈希函数,其中16777619是FNV质数,84696351是偏移基数。该函数应用于表达式数据地址的
Murmur3-32哈希值(MMA使用指针以保留每个数据的一个实例)。最终,地址被解析为值 - 对于简单的机器整数,该值是立即的,对于其他类型则有点棘手。实际上,Murmur3-32实现函数包含一个附加参数(默认为4,特殊情况下与维基百科中的行为相同),该参数选择从输入的表达式结构中选择多少位。由于正常表达式在内部表示为指针数组,因此可以通过重复添加4(字节= 32位)到表达式的基指针来取第一个、第二个等等。因此,将8传递给函数将给出第二个指针,12为第三个,依此类推。由于内部结构(大整数、机器整数、机器实数、大实数等)具有不同的成员变量(例如,机器整数仅具有指向int的指针,而复杂的2个指向数字的指针等),因此对于每个表达式结构都有一个“包装器”,将其内部成员组合成一个单一的32位哈希(基本上使用FNV-1轮)。最简单的要哈希的表达式是整数。
"
murmur3_32()
函数的种子为1131470165,n=0,其他参数与维基百科相同。
因此我们有:
"
hash_of_number = 16777619 * (84696351 ^ murmur3_32( &number ))
在这里,“ ^ ”表示异或运算。我并没有尝试过这个方法——指针是使用WINAPI EncodePointer()
进行编码的,因此无法在运行时被利用。(也许值得在Wine下的Linux中运行,并使用修改版的EncodePonter
?)
64位
它使用FNV-1 64位哈希函数,偏移基础为0xAF63BD4C8601B7DF,FNV质数为0x100000001B3,以及SIP64-24哈希(这里是参考代码),其中第一个64位为k0,最后一个64位为k1。该函数应用于表达式的基指针并在内部解析。与32位的murmur3一样,还有一个额外参数(默认为8)来选择从输入表达式结构中选择多少个指针。对于每种表达式类型,都有一个包装器通过FNV-1 64位轮来将结构成员压缩为单个哈希。
对于机器整数,我们有:
hash_number_64bit = 0x100000001B3 * (0xAF63BD4C8601B7DF ^ SIP64_24( &number ))
再次强调,我并没有真正尝试过。有人可以试试吗?
不适合胆小者
如果您查看他们的 内部实现笔记,他们说:“每个表达式都包含一种特殊形式的哈希码,用于模式匹配和评估。”
他们所指的哈希码是由这些函数生成的-在正常表达式包装函数中的某个点上,有一个赋值将计算出的哈希放置在表达式结构体本身内。
了解他们如何利用这些哈希进行模式匹配肯定很酷。因此,我尝试运行bigInteger包装器以查看会发生什么-那是最简单的复合表达式。
它开始检查返回1的某些内容-不知道是什么。
所以它执行
var1 = 16777619 * (67918732 ^ hashMachineInteger(1));
使用hashMachineInteger()是我们之前提到的 - 包括值。
然后它从结构体中读取bigInt的字节长度(bignum_length
),并运行。
result = 16777619 * (v10 ^ murmur3_32(v6, 4 * v4));
请注意,如果
4 * bignum_length
大于8,则调用
murmur3_32()
(可能与机器整数的最大值
$MaxMachineNumber
2^32^32
有关,反之则是一个bigInt应该是什么)。
因此,最终代码如下:
if (bignum_length > 8){
result = 16777619 * (16777619 * (67918732 ^ ( 16777619 * (84696351 ^ murmur3_32( 1, 4 )))) ^ murmur3_32( &bignum, 4 * bignum_length ));
}
我对这个结构的属性做出了一些假设。存在许多异或门,并且
16777619 + 67918732 = 84696351
的事实可能使人们认为某种循环结构被利用来检查模式——即减去偏移量并除以质数,或者类似于此的东西。软件Cassandra使用Murmur哈希算法进行令牌生成——请参见
这些图片,了解我所说的“循环结构”。也许每个表达式都使用不同的质数——还需要检查。
希望能帮到你
Hash[expr]
的默认行为和在不同版本的Mathematica中的哈希"。 - Alexey Popkov