go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/murmur3/murmur128.go (about)

     1  /*
     2  
     3  Copyright (c) 2023 - Present. Will Charczuk. All rights reserved.
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository.
     5  
     6  */
     7  
     8  package murmur3
     9  
    10  import (
    11  	"hash"
    12  	"math/bits"
    13  	"unsafe"
    14  )
    15  
    16  const (
    17  	c1_128 = 0x87c37b91114253d5
    18  	c2_128 = 0x4cf5ad432745937f
    19  )
    20  
    21  // Make sure interfaces are correctly implemented.
    22  var (
    23  	_ hash.Hash = new(digest128)
    24  	_ Hash128   = new(digest128)
    25  	_ bmixer    = new(digest128)
    26  )
    27  
    28  // Hash128 represents a 128-bit hasher
    29  // Hack: the standard api doesn't define any Hash128 interface.
    30  type Hash128 interface {
    31  	hash.Hash
    32  	Sum128() (uint64, uint64)
    33  }
    34  
    35  // digest128 represents a partial evaluation of a 128 bites hash.
    36  type digest128 struct {
    37  	digest
    38  	h1 uint64 // Unfinalized running hash part 1.
    39  	h2 uint64 // Unfinalized running hash part 2.
    40  }
    41  
    42  // New128 returns a 128-bit hasher
    43  func New128() Hash128 { return New128WithSeed(0) }
    44  
    45  // New128WithSeed returns a 128-bit hasher set with explicit seed value
    46  func New128WithSeed(seed uint32) Hash128 {
    47  	d := new(digest128)
    48  	d.seed = seed
    49  	d.bmixer = d
    50  	d.Reset()
    51  	return d
    52  }
    53  
    54  func (d *digest128) Size() int { return 16 }
    55  
    56  func (d *digest128) reset() { d.h1, d.h2 = uint64(d.seed), uint64(d.seed) }
    57  
    58  func (d *digest128) Sum(b []byte) []byte {
    59  	h1, h2 := d.Sum128()
    60  	return append(b,
    61  		byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
    62  		byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1),
    63  
    64  		byte(h2>>56), byte(h2>>48), byte(h2>>40), byte(h2>>32),
    65  		byte(h2>>24), byte(h2>>16), byte(h2>>8), byte(h2),
    66  	)
    67  }
    68  
    69  func (d *digest128) bmix(p []byte) (tail []byte) {
    70  	h1, h2 := d.h1, d.h2
    71  
    72  	nblocks := len(p) / 16
    73  	for i := 0; i < nblocks; i++ {
    74  		t := (*[2]uint64)(unsafe.Pointer(&p[i*16]))
    75  		k1, k2 := t[0], t[1]
    76  
    77  		k1 *= c1_128
    78  		k1 = bits.RotateLeft64(k1, 31)
    79  		k1 *= c2_128
    80  		h1 ^= k1
    81  
    82  		h1 = bits.RotateLeft64(h1, 27)
    83  		h1 += h2
    84  		h1 = h1*5 + 0x52dce729
    85  
    86  		k2 *= c2_128
    87  		k2 = bits.RotateLeft64(k2, 33)
    88  		k2 *= c1_128
    89  		h2 ^= k2
    90  
    91  		h2 = bits.RotateLeft64(h2, 31)
    92  		h2 += h1
    93  		h2 = h2*5 + 0x38495ab5
    94  	}
    95  	d.h1, d.h2 = h1, h2
    96  	return p[nblocks*d.Size():]
    97  }
    98  
    99  func (d *digest128) Sum128() (h1, h2 uint64) {
   100  
   101  	h1, h2 = d.h1, d.h2
   102  
   103  	var k1, k2 uint64
   104  	switch len(d.tail) & 15 {
   105  	case 15:
   106  		k2 ^= uint64(d.tail[14]) << 48
   107  		fallthrough
   108  	case 14:
   109  		k2 ^= uint64(d.tail[13]) << 40
   110  		fallthrough
   111  	case 13:
   112  		k2 ^= uint64(d.tail[12]) << 32
   113  		fallthrough
   114  	case 12:
   115  		k2 ^= uint64(d.tail[11]) << 24
   116  		fallthrough
   117  	case 11:
   118  		k2 ^= uint64(d.tail[10]) << 16
   119  		fallthrough
   120  	case 10:
   121  		k2 ^= uint64(d.tail[9]) << 8
   122  		fallthrough
   123  	case 9:
   124  		k2 ^= uint64(d.tail[8]) << 0
   125  
   126  		k2 *= c2_128
   127  		k2 = bits.RotateLeft64(k2, 33)
   128  		k2 *= c1_128
   129  		h2 ^= k2
   130  
   131  		fallthrough
   132  
   133  	case 8:
   134  		k1 ^= uint64(d.tail[7]) << 56
   135  		fallthrough
   136  	case 7:
   137  		k1 ^= uint64(d.tail[6]) << 48
   138  		fallthrough
   139  	case 6:
   140  		k1 ^= uint64(d.tail[5]) << 40
   141  		fallthrough
   142  	case 5:
   143  		k1 ^= uint64(d.tail[4]) << 32
   144  		fallthrough
   145  	case 4:
   146  		k1 ^= uint64(d.tail[3]) << 24
   147  		fallthrough
   148  	case 3:
   149  		k1 ^= uint64(d.tail[2]) << 16
   150  		fallthrough
   151  	case 2:
   152  		k1 ^= uint64(d.tail[1]) << 8
   153  		fallthrough
   154  	case 1:
   155  		k1 ^= uint64(d.tail[0]) << 0
   156  		k1 *= c1_128
   157  		k1 = bits.RotateLeft64(k1, 31)
   158  		k1 *= c2_128
   159  		h1 ^= k1
   160  	}
   161  
   162  	h1 ^= uint64(d.clen)
   163  	h2 ^= uint64(d.clen)
   164  
   165  	h1 += h2
   166  	h2 += h1
   167  
   168  	h1 = fmix64(h1)
   169  	h2 = fmix64(h2)
   170  
   171  	h1 += h2
   172  	h2 += h1
   173  
   174  	return h1, h2
   175  }
   176  
   177  func fmix64(k uint64) uint64 {
   178  	k ^= k >> 33
   179  	k *= 0xff51afd7ed558ccd
   180  	k ^= k >> 33
   181  	k *= 0xc4ceb9fe1a85ec53
   182  	k ^= k >> 33
   183  	return k
   184  }
   185  
   186  // Sum128 returns the MurmurHash3 sum of data. It is equivalent to the
   187  // following sequence (without the extra burden and the extra allocation):
   188  //
   189  //	hasher := New128()
   190  //	hasher.Write(data)
   191  //	return hasher.Sum128()
   192  func Sum128(data []byte) (h1 uint64, h2 uint64) { return Sum128WithSeed(data, 0) }
   193  
   194  // Sum128WithSeed returns the MurmurHash3 sum of data. It is equivalent to the
   195  // following sequence (without the extra burden and the extra allocation):
   196  //
   197  //	hasher := New128WithSeed(seed)
   198  //	hasher.Write(data)
   199  //	return hasher.Sum128()
   200  func Sum128WithSeed(data []byte, seed uint32) (h1 uint64, h2 uint64) {
   201  	d := digest128{h1: uint64(seed), h2: uint64(seed)}
   202  	d.seed = seed
   203  	d.tail = d.bmix(data)
   204  	d.clen = len(data)
   205  	return d.Sum128()
   206  }