utils/hash.go (159 lines of code) (raw):
// Copyright (c) 2017-2018 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package utils
import "unsafe"
const (
murmur3C1_32 uint32 = 0xcc9e2d51
murmur3C2_32 uint32 = 0x1b873593
)
// Murmur3Sum32 implements Murmur3Sum32 hash algorithm
func Murmur3Sum32(key unsafe.Pointer, bytes int, seed uint32) uint32 {
h1 := seed
nblocks := bytes / 4
var p uintptr
if bytes > 0 {
p = uintptr(key)
}
p1 := p + uintptr(4*nblocks)
for ; p < p1; p += 4 {
k1 := *(*uint32)(unsafe.Pointer(p))
k1 *= murmur3C1_32
k1 = (k1 << 15) | (k1 >> 17)
k1 *= murmur3C2_32
h1 ^= k1
h1 = (h1 << 13) | (h1 >> 19)
h1 = h1*5 + 0xe6546b64
}
tailBytes := bytes - nblocks*4
tail := p1
var k1 uint32
switch tailBytes & 3 {
case 3:
k1 ^= uint32(*(*uint8)(unsafe.Pointer(tail + uintptr(2)))) << 16
fallthrough
case 2:
k1 ^= uint32(*(*uint8)(unsafe.Pointer(tail + uintptr(1)))) << 8
fallthrough
case 1:
k1 ^= uint32(*(*uint8)(unsafe.Pointer(tail + uintptr(0))))
k1 *= murmur3C1_32
k1 = (k1 << 15) | (k1 >> 17)
k1 *= murmur3C2_32
h1 ^= k1
}
h1 ^= uint32(bytes)
h1 ^= h1 >> 16
h1 *= 0x85ebca6b
h1 ^= h1 >> 13
h1 *= 0xc2b2ae35
h1 ^= h1 >> 16
return h1
}
func rotl64(x uint64, r int8) uint64 {
return (x << uint64(r)) | (x >> (64 - uint64(r)))
}
func fmix64(k uint64) uint64 {
k ^= k >> 33
k *= 0xff51afd7ed558ccd
k ^= k >> 33
k *= 0xc4ceb9fe1a85ec53
k ^= k >> 33
return k
}
// Murmur3Sum128 implements murmur3sum128 hash algorithm
func Murmur3Sum128(key unsafe.Pointer, bytes int, seed uint32) (out [2]uint64) {
var data = uintptr(key)
nblocks := bytes / 16
var i int
var h1 = uint64(seed)
var h2 = uint64(seed)
var c1 uint64 = 0x87c37b91114253d5
var c2 uint64 = 0x4cf5ad432745937f
blocks := data
for i = 0; i < nblocks; i++ {
k1 := *(*uint64)(unsafe.Pointer(blocks + uintptr(i*2)*8))
k2 := *(*uint64)(unsafe.Pointer(blocks + uintptr(i*2+1)*8))
k1 *= c1
k1 = rotl64(k1, 31)
k1 *= c2
h1 ^= k1
h1 = rotl64(h1, 27)
h1 += h2
h1 = h1*5 + 0x52dce729
k2 *= c2
k2 = rotl64(k2, 33)
k2 *= c1
h2 ^= k2
h2 = rotl64(h2, 31)
h2 += h1
h2 = h2*5 + 0x38495ab5
}
tail := data + uintptr(nblocks)*16
var k1 uint64 = 0
var k2 uint64 = 0
switch bytes & 15 {
case 15:
k2 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(14)))) << 48
fallthrough
case 14:
k2 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(13)))) << 40
fallthrough
case 13:
k2 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(12)))) << 32
fallthrough
case 12:
k2 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(11)))) << 24
fallthrough
case 11:
k2 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(10)))) << 16
fallthrough
case 10:
k2 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(9)))) << 8
fallthrough
case 9:
k2 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(8))))
k2 *= c2
k2 = rotl64(k2, 33)
k2 *= c1
h2 ^= k2
fallthrough
case 8:
k1 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(7)))) << 56
fallthrough
case 7:
k1 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(6)))) << 48
fallthrough
case 6:
k1 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(5)))) << 40
fallthrough
case 5:
k1 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(4)))) << 32
fallthrough
case 4:
k1 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(3)))) << 24
fallthrough
case 3:
k1 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(2)))) << 16
fallthrough
case 2:
k1 ^= uint64(*(*uint8)(unsafe.Pointer(tail + uintptr(1)))) << 8
fallthrough
case 1:
k1 ^= uint64(*(*uint8)(unsafe.Pointer(tail)))
k1 *= c1
k1 = rotl64(k1, 31)
k1 *= c2
h1 ^= k1
}
h1 ^= uint64(bytes)
h2 ^= uint64(bytes)
h1 += h2
h2 += h1
h1 = fmix64(h1)
h2 = fmix64(h2)
h1 += h2
h2 += h1
out[0] = h1
out[1] = h2
return
}
// Murmur3Sum64 use Murmur3Sum128 to generate 64bit hash
func Murmur3Sum64(key unsafe.Pointer, bytes int, seed uint32) uint64 {
out := Murmur3Sum128(key, bytes, seed)
return out[0]
}