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

     1  package murmur3
     2  
     3  // http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/hash/Murmur3_32HashFunction.java
     4  
     5  import (
     6  	"fmt"
     7  	"unsafe"
     8  )
     9  
    10  const (
    11  	c1_32 uint32 = 0xcc9e2d51
    12  	c2_32 uint32 = 0x1b873593
    13  )
    14  
    15  // Digest32 represents a partial evaluation of a 32 bits hash.
    16  type Digest32 struct {
    17  	digest
    18  	h1 uint32 // Unfinalized running hash.
    19  }
    20  
    21  // New32 returns new 32-bit hasher
    22  func New32() Digest32 { return New32WithSeed(0) }
    23  
    24  // New32WithSeed returns new 32-bit hasher set with explicit seed value
    25  func New32WithSeed(seed uint32) Digest32 {
    26  	return Digest32{digest: digest{seed: seed}, h1: seed}
    27  }
    28  
    29  func (d Digest32) Size() int {
    30  	return 4
    31  }
    32  
    33  func (d Digest32) Sum(b []byte) []byte {
    34  	h := d.Sum32()
    35  	return append(b, byte(h>>24), byte(h>>16), byte(h>>8), byte(h))
    36  }
    37  
    38  // Digest as many blocks as possible.
    39  func (d Digest32) bmix(p []byte) (Digest32, []byte) {
    40  	h1 := d.h1
    41  
    42  	nblocks := len(p) / 4
    43  	for i := 0; i < nblocks; i++ {
    44  		k1 := *(*uint32)(unsafe.Pointer(&p[i*4]))
    45  
    46  		k1 *= c1_32
    47  		k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
    48  		k1 *= c2_32
    49  
    50  		h1 ^= k1
    51  		h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13)
    52  		h1 = h1*4 + h1 + 0xe6546b64
    53  	}
    54  	d.h1 = h1
    55  	return d, p[nblocks*d.Size():]
    56  }
    57  
    58  func (d Digest32) bmixbuf() Digest32 {
    59  	h1 := d.h1
    60  
    61  	if d.tail != d.Size() {
    62  		panic(fmt.Errorf("expected full block"))
    63  	}
    64  
    65  	k1 := d.loadUint32(0)
    66  
    67  	k1 *= c1_32
    68  	k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
    69  	k1 *= c2_32
    70  
    71  	h1 ^= k1
    72  	h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13)
    73  	h1 = h1*4 + h1 + 0xe6546b64
    74  
    75  	d.h1 = h1
    76  	d.tail = 0
    77  	return d
    78  }
    79  
    80  func (d Digest32) Sum32() (h1 uint32) {
    81  
    82  	h1 = d.h1
    83  
    84  	var k1 uint32
    85  	switch d.tail & 3 {
    86  	case 3:
    87  		k1 ^= uint32(d.buf[2]) << 16
    88  		fallthrough
    89  	case 2:
    90  		k1 ^= uint32(d.buf[1]) << 8
    91  		fallthrough
    92  	case 1:
    93  		k1 ^= uint32(d.buf[0])
    94  		k1 *= c1_32
    95  		k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
    96  		k1 *= c2_32
    97  		h1 ^= k1
    98  	}
    99  
   100  	h1 ^= uint32(d.clen)
   101  
   102  	h1 ^= h1 >> 16
   103  	h1 *= 0x85ebca6b
   104  	h1 ^= h1 >> 13
   105  	h1 *= 0xc2b2ae35
   106  	h1 ^= h1 >> 16
   107  
   108  	return h1
   109  }
   110  
   111  // Write will write bytes to the digest and return a new digest
   112  // representing the derived digest.
   113  func (d Digest32) Write(p []byte) Digest32 {
   114  	n := len(p)
   115  	d.clen += n
   116  
   117  	if d.tail > 0 {
   118  		// Stick back pending bytes.
   119  		nfree := d.Size() - d.tail // nfree ∈ [1, d.size-1].
   120  		if len(p) <= int(nfree) {
   121  			// Everything can fit in buf
   122  			for i := 0; i < len(p); i++ {
   123  				d.buf[d.tail] = p[i]
   124  				d.tail++
   125  			}
   126  			if len(p) == int(nfree) {
   127  				d = d.bmixbuf()
   128  			}
   129  			return d
   130  		}
   131  
   132  		// One full block can be formed.
   133  		add := p[:nfree]
   134  		for i := 0; i < len(add); i++ {
   135  			d.buf[d.tail] = add[i]
   136  			d.tail++
   137  		}
   138  
   139  		// Process the full block
   140  		d = d.bmixbuf()
   141  
   142  		p = p[nfree:]
   143  	}
   144  
   145  	d, tail := d.bmix(p)
   146  
   147  	// Keep own copy of the 0 to Size()-1 pending bytes.
   148  	d.tail = len(tail)
   149  	for i := 0; i < d.tail; i++ {
   150  		d.buf[i] = tail[i]
   151  	}
   152  
   153  	return d
   154  }
   155  
   156  /*
   157  func rotl32(x uint32, r byte) uint32 {
   158  	return (x << r) | (x >> (32 - r))
   159  }
   160  */
   161  
   162  // Sum32 returns the MurmurHash3 sum of data. It is equivalent to the
   163  // following sequence (without the extra burden and the extra allocation):
   164  //     hasher := New32()
   165  //     hasher.Write(data)
   166  //     return hasher.Sum32()
   167  func Sum32(data []byte) uint32 { return Sum32WithSeed(data, 0) }
   168  
   169  // Sum32WithSeed returns the MurmurHash3 sum of data. It is equivalent to the
   170  // following sequence (without the extra burden and the extra allocation):
   171  //     hasher := New32WithSeed(seed)
   172  //     hasher.Write(data)
   173  //     return hasher.Sum32()
   174  func Sum32WithSeed(data []byte, seed uint32) uint32 {
   175  	h1 := seed
   176  
   177  	nblocks := len(data) / 4
   178  	var p uintptr
   179  	if len(data) > 0 {
   180  		p = uintptr(unsafe.Pointer(&data[0]))
   181  	}
   182  	p1 := p + uintptr(4*nblocks)
   183  	for ; p < p1; p += 4 {
   184  		k1 := *(*uint32)(unsafe.Pointer(p))
   185  
   186  		k1 *= c1_32
   187  		k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
   188  		k1 *= c2_32
   189  
   190  		h1 ^= k1
   191  		h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13)
   192  		h1 = h1*4 + h1 + 0xe6546b64
   193  	}
   194  
   195  	tail := data[nblocks*4:]
   196  
   197  	var k1 uint32
   198  	switch len(tail) & 3 {
   199  	case 3:
   200  		k1 ^= uint32(tail[2]) << 16
   201  		fallthrough
   202  	case 2:
   203  		k1 ^= uint32(tail[1]) << 8
   204  		fallthrough
   205  	case 1:
   206  		k1 ^= uint32(tail[0])
   207  		k1 *= c1_32
   208  		k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
   209  		k1 *= c2_32
   210  		h1 ^= k1
   211  	}
   212  
   213  	h1 ^= uint32(len(data))
   214  
   215  	h1 ^= h1 >> 16
   216  	h1 *= 0x85ebca6b
   217  	h1 ^= h1 >> 13
   218  	h1 *= 0xc2b2ae35
   219  	h1 ^= h1 >> 16
   220  
   221  	return h1
   222  }