Murmur3哈希在Go和Python之间的兼容性

4
我们有两个不同的库,一个是Python,一个是Go,需要以相同的方式计算murmur3哈希。不幸的是,无论我们如何努力,都无法让这些库产生相同的结果。从这个关于Java和Python的SO问题中可以看出,兼容性并不一定是直截了当的。
现在我们正在使用python mmh3Go github.com/spaolacci/murmur3库。
在Go中:
hash := murmur3.New128()
hash.Write([]byte("chocolate-covered-espresso-beans"))
fmt.Println(base64.RawURLEncoding.EncodeToString(hash.Sum(nil)))
// Output: cLHSo2nCBxyOezviLM5gwg

在Python中:

name = "chocolate-covered-espresso-beans"
hash = mmh3.hash128(name.encode('utf-8'), signed=False).to_bytes(16, byteorder='big', signed=False)
print(base64.urlsafe_b64encode(hash).decode('utf-8').strip("="))
# Output: jns74izOYMJwsdKjacIHHA (big byteorder)

hash = mmh3.hash128(name.encode('utf-8'), signed=False).to_bytes(16, byteorder='little', signed=False)
print(base64.urlsafe_b64encode(hash).decode('utf-8').strip("="))
# Output: HAfCaaPSsXDCYM4s4jt7jg (little byteorder)

hash = mmh3.hash_bytes(name.encode('utf-8'))
print(base64.urlsafe_b64encode(hash).decode('utf-8').strip("="))
# Output: HAfCaaPSsXDCYM4s4jt7jg

在Go语言中,murmur3 返回一个 uint64,因此我们假设在Python中 signed=False;然而我们也尝试过 signed=True,但并未获得匹配的哈希值。
我们可以使用不同的库,但是我们想知道从字符串计算base64编码哈希值时,我们的Go或Python方法是否存在问题。感谢任何帮助。

这是我们正在进行各种测试的要点,如果有帮助的话:https://gist.github.com/bbengfort/fed9f92142b31b0261fc71fdb0d168a5 - bbengfort
1
MurmurHash3算法有两个不同的版本,一个针对x86优化,另一个针对x64优化,它们产生不同的哈希值。在两个平台上可能没有使用相同的算法。 - user2357112
好的想法;我假设x64优化会在我的机器上使用,但也许我应该更详细地检查一下。 - bbengfort
它们不都会产生整数吗?为什么不查看它们,而要添加多个步骤将它们转换为字符串呢?也许区别实际上在于那些额外的步骤... - Kelly Bundy
你的 Go 代码无效。 - Kelly Bundy
啊,抱歉忘记了括号 - 现在已经修复了。我们确实检查过编码是否是问题,使用长十进制、十六进制和base64编码返回的128位数字,并且我们仍然无法解决哈希值不匹配的问题。 - bbengfort
1个回答

3

那个第一次的Python结果几乎正确。

>>> binascii.hexlify(base64.b64decode('jns74izOYMJwsdKjacIHHA=='))
b'8e7b3be22cce60c270b1d2a369c2071c'

在Go中:

    x, y := murmur3.Sum128([]byte("chocolate-covered-espresso-beans"))
    fmt.Printf("%x %x\n", x, y)

结果为:

70b1d2a369c2071c 8e7b3be22cce60c2

所以这两个单词的顺序被颠倒了。为了在Python中得到相同的结果,您可以尝试类似以下方式:

name = "chocolate-covered-espresso-beans"
hash = mmh3.hash128(name.encode('utf-8'), signed=False).to_bytes(16, byteorder='big', signed=False)
hash = hash[8:] + hash[:8]
print(base64.urlsafe_b64encode(hash).decode('utf-8').strip("="))
# cLHSo2nCBxyOezviLM5gwg

1
好眼力;不确定你是怎么发现的!我猜Go和Python的murmur128都使用了2个64位无符号整数,但是它们返回的顺序不同。看起来这可能会导致一些与基本规范有关的问题。 - bbengfort
1
在过去的处理中,这已经成为我必须“先检查”的事情之一。以十六进制形式查看数据可以更容易地发现此类问题。 - kichik
我已在Go和Python代码库中都打开了问题,以查看是否有办法修复与murmur3参考代码相关的问题。 - bbengfort

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接