github.com/m3db/stackmurmur3@v1.0.1/murmur128.go (about)

     1  package murmur3
     2  
     3  import (
     4  	"fmt"
     5  	"unsafe"
     6  )
     7  
     8  const (
     9  	c1_128 = 0x87c37b91114253d5
    10  	c2_128 = 0x4cf5ad432745937f
    11  )
    12  
    13  // Digest128 is a murmur3 128 bit digest that can be written to
    14  // as many consequent times as necessary without heap allocations.
    15  type Digest128 struct {
    16  	digest
    17  	h1 uint64 // Unfinalized running hash part 1.
    18  	h2 uint64 // Unfinalized running hash part 2.
    19  }
    20  
    21  // New128 returns a new 128 bit digest.
    22  func New128() Digest128 {
    23  	return New128WithSeed(0)
    24  }
    25  
    26  // New128WithSeed returns a new 128 bit digest with a seed.
    27  func New128WithSeed(seed uint32) Digest128 {
    28  	s := uint64(seed)
    29  	return Digest128{digest: digest{seed: seed}, h1: s, h2: s}
    30  }
    31  
    32  // Size returns the number of bytes Sum will return.
    33  func (d Digest128) Size() int {
    34  	return 16
    35  }
    36  
    37  // Sum returns the current binary hash appended to input byte ref.
    38  func (d Digest128) Sum(b []byte) []byte {
    39  	h1, h2 := d.Sum128()
    40  	return append(b,
    41  		byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
    42  		byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1),
    43  
    44  		byte(h2>>56), byte(h2>>48), byte(h2>>40), byte(h2>>32),
    45  		byte(h2>>24), byte(h2>>16), byte(h2>>8), byte(h2),
    46  	)
    47  }
    48  
    49  func (d Digest128) bmix(p []byte) (Digest128, []byte) {
    50  	h1, h2 := d.h1, d.h2
    51  
    52  	nblocks := len(p) / 16
    53  	for i := 0; i < nblocks; i++ {
    54  		t := (*[2]uint64)(unsafe.Pointer(&p[i*16]))
    55  		k1, k2 := t[0], t[1]
    56  
    57  		k1 *= c1_128
    58  		k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31)
    59  		k1 *= c2_128
    60  		h1 ^= k1
    61  
    62  		h1 = (h1 << 27) | (h1 >> 37) // rotl64(h1, 27)
    63  		h1 += h2
    64  		h1 = h1*5 + 0x52dce729
    65  
    66  		k2 *= c2_128
    67  		k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33)
    68  		k2 *= c1_128
    69  		h2 ^= k2
    70  
    71  		h2 = (h2 << 31) | (h2 >> 33) // rotl64(h2, 31)
    72  		h2 += h1
    73  		h2 = h2*5 + 0x38495ab5
    74  	}
    75  	d.h1, d.h2 = h1, h2
    76  	return d, p[nblocks*d.Size():]
    77  }
    78  
    79  func (d Digest128) bmixbuf() Digest128 {
    80  	h1, h2 := d.h1, d.h2
    81  
    82  	if d.tail != d.Size() {
    83  		panic(fmt.Errorf("expected full block"))
    84  	}
    85  
    86  	k1, k2 := d.loadUint64(0), d.loadUint64(1)
    87  
    88  	k1 *= c1_128
    89  	k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31)
    90  	k1 *= c2_128
    91  	h1 ^= k1
    92  
    93  	h1 = (h1 << 27) | (h1 >> 37) // rotl64(h1, 27)
    94  	h1 += h2
    95  	h1 = h1*5 + 0x52dce729
    96  
    97  	k2 *= c2_128
    98  	k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33)
    99  	k2 *= c1_128
   100  	h2 ^= k2
   101  
   102  	h2 = (h2 << 31) | (h2 >> 33) // rotl64(h2, 31)
   103  	h2 += h1
   104  	h2 = h2*5 + 0x38495ab5
   105  
   106  	d.h1, d.h2 = h1, h2
   107  	d.tail = 0
   108  	return d
   109  }
   110  
   111  // Sum128 returns the 128 bit hash of the digest.
   112  func (d Digest128) Sum128() (h1, h2 uint64) {
   113  	h1, h2 = d.h1, d.h2
   114  
   115  	var k1, k2 uint64
   116  	switch d.tail & 15 {
   117  	case 15:
   118  		k2 ^= uint64(d.buf[14]) << 48
   119  		fallthrough
   120  	case 14:
   121  		k2 ^= uint64(d.buf[13]) << 40
   122  		fallthrough
   123  	case 13:
   124  		k2 ^= uint64(d.buf[12]) << 32
   125  		fallthrough
   126  	case 12:
   127  		k2 ^= uint64(d.buf[11]) << 24
   128  		fallthrough
   129  	case 11:
   130  		k2 ^= uint64(d.buf[10]) << 16
   131  		fallthrough
   132  	case 10:
   133  		k2 ^= uint64(d.buf[9]) << 8
   134  		fallthrough
   135  	case 9:
   136  		k2 ^= uint64(d.buf[8]) << 0
   137  
   138  		k2 *= c2_128
   139  		k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33)
   140  		k2 *= c1_128
   141  		h2 ^= k2
   142  
   143  		fallthrough
   144  
   145  	case 8:
   146  		k1 ^= uint64(d.buf[7]) << 56
   147  		fallthrough
   148  	case 7:
   149  		k1 ^= uint64(d.buf[6]) << 48
   150  		fallthrough
   151  	case 6:
   152  		k1 ^= uint64(d.buf[5]) << 40
   153  		fallthrough
   154  	case 5:
   155  		k1 ^= uint64(d.buf[4]) << 32
   156  		fallthrough
   157  	case 4:
   158  		k1 ^= uint64(d.buf[3]) << 24
   159  		fallthrough
   160  	case 3:
   161  		k1 ^= uint64(d.buf[2]) << 16
   162  		fallthrough
   163  	case 2:
   164  		k1 ^= uint64(d.buf[1]) << 8
   165  		fallthrough
   166  	case 1:
   167  		k1 ^= uint64(d.buf[0]) << 0
   168  		k1 *= c1_128
   169  		k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31)
   170  		k1 *= c2_128
   171  		h1 ^= k1
   172  	}
   173  
   174  	h1 ^= uint64(d.clen)
   175  	h2 ^= uint64(d.clen)
   176  
   177  	h1 += h2
   178  	h2 += h1
   179  
   180  	h1 = fmix64(h1)
   181  	h2 = fmix64(h2)
   182  
   183  	h1 += h2
   184  	h2 += h1
   185  
   186  	return h1, h2
   187  }
   188  
   189  // Write will write bytes to the digest and return a new digest
   190  // representing the derived digest.
   191  func (d Digest128) Write(p []byte) Digest128 {
   192  	n := len(p)
   193  	d.clen += n
   194  
   195  	if d.tail > 0 {
   196  		// Stick back pending bytes.
   197  		nfree := d.Size() - d.tail // nfree ∈ [1, d.Size()-1].
   198  		if len(p) <= nfree {
   199  			// Everything can fit in buf
   200  			for i := 0; i < len(p); i++ {
   201  				d.buf[d.tail] = p[i]
   202  				d.tail++
   203  			}
   204  			if len(p) == int(nfree) {
   205  				d = d.bmixbuf()
   206  			}
   207  			return d
   208  		}
   209  
   210  		// One full block can be formed.
   211  		add := p[:nfree]
   212  		for i := 0; i < len(add); i++ {
   213  			d.buf[d.tail] = add[i]
   214  			d.tail++
   215  		}
   216  
   217  		// Process the full block
   218  		d = d.bmixbuf()
   219  
   220  		p = p[nfree:]
   221  	}
   222  
   223  	d, tail := d.bmix(p)
   224  
   225  	// Keep own copy of the 0 to Size()-1 pending bytes.
   226  	d.tail = len(tail)
   227  	for i := 0; i < d.tail; i++ {
   228  		d.buf[i] = tail[i]
   229  	}
   230  
   231  	return d
   232  }
   233  
   234  func fmix64(k uint64) uint64 {
   235  	k ^= k >> 33
   236  	k *= 0xff51afd7ed558ccd
   237  	k ^= k >> 33
   238  	k *= 0xc4ceb9fe1a85ec53
   239  	k ^= k >> 33
   240  	return k
   241  }
   242  
   243  // Sum128 returns the MurmurHash3 sum of data without any heap allocations.
   244  func Sum128(data []byte) (h1 uint64, h2 uint64) {
   245  	return Sum128WithSeed(data, 0)
   246  }
   247  
   248  // Sum128WithSeed returns the MurmurHash3 sum of data given a seed
   249  // without any heap allocations.
   250  func Sum128WithSeed(data []byte, seed uint32) (h1 uint64, h2 uint64) {
   251  	return New128WithSeed(seed).Write(data).Sum128()
   252  }