github.com/scottcagno/storage@v1.8.0/pkg/hash/cityhash/helpers.go (about)

     1  /*
     2   * // Copyright (c) 2021. Scott Cagno. All rights reserved.
     3   * // The license can be found in the root of this project; see LICENSE.
     4   */
     5  
     6  package cityhash
     7  
     8  import "encoding/binary"
     9  
    10  // A 32-bit to 32-bit integer hash copied from Murmur3.
    11  func fmix(h uint32) uint32 {
    12  	h ^= h >> 16
    13  	h *= 0x85ebca6b
    14  	h ^= h >> 13
    15  	h *= 0xc2b2ae35
    16  	h ^= h >> 16
    17  	return h
    18  }
    19  
    20  // Bitwise right rotate, 64-bit.
    21  func ror64(val, shift uint64) uint64 {
    22  	// Avoid shifting by 64: doing so yields an undefined result.
    23  	if shift != 0 {
    24  		return val>>shift | val<<(64-shift)
    25  	}
    26  	return val
    27  }
    28  
    29  // Bitwise right rotate, 32-bit.
    30  func ror32(val, shift uint32) uint32 {
    31  	// Avoid shifting by 32: doing so yields an undefined result.
    32  	if shift != 0 {
    33  		return val>>shift | val<<(32-shift)
    34  	}
    35  	return val
    36  }
    37  
    38  func shiftMix(val uint64) uint64 { return val ^ (val >> 47) }
    39  
    40  func hash64Len16(u, v uint64) uint64 { return hash128to64(u, v) }
    41  
    42  func hash64Len16Mul(u, v, mul uint64) uint64 {
    43  	// Murmur-inspired hashing.
    44  	a := (u ^ v) * mul
    45  	a ^= a >> 47
    46  	b := (v ^ a) * mul
    47  	b ^= b >> 47
    48  	b *= mul
    49  	return b
    50  }
    51  
    52  func hash64Len0to16(s []byte) uint64 {
    53  	n := uint64(len(s))
    54  	if n >= 8 {
    55  		mul := k2 + n*2
    56  		a := fetch64(s) + k2
    57  		b := fetch64(s[n-8:])
    58  		c := ror64(b, 37)*mul + a
    59  		d := (ror64(a, 25) + b) * mul
    60  		return hash64Len16Mul(c, d, mul)
    61  	}
    62  	if n >= 4 {
    63  		mul := k2 + n*2
    64  		a := uint64(fetch32(s))
    65  		return hash64Len16Mul(n+(a<<3), uint64(fetch32(s[n-4:])), mul)
    66  	}
    67  	if n > 0 {
    68  		a := s[0]
    69  		b := s[n>>1]
    70  		c := s[n-1]
    71  		y := uint32(a) + uint32(b)<<8
    72  		z := uint32(n) + uint32(c)<<2
    73  		return shiftMix(uint64(y)*k2^uint64(z)*k0) * k2
    74  	}
    75  	return k2
    76  }
    77  
    78  func hash64Len17to32(s []byte) uint64 {
    79  	n := uint64(len(s))
    80  	mul := k2 + n*2
    81  	a := fetch64(s) * k1
    82  	b := fetch64(s[8:])
    83  	c := fetch64(s[n-8:]) * mul
    84  	d := fetch64(s[n-16:]) * k2
    85  	return hash64Len16Mul(ror64(a+b, 43)+ror64(c, 30)+d, a+ror64(b+k2, 18)+c, mul)
    86  }
    87  
    88  func bswap64(in uint64) uint64 {
    89  	var buf [8]byte
    90  	binary.LittleEndian.PutUint64(buf[:], in)
    91  	return binary.BigEndian.Uint64(buf[:])
    92  }
    93  
    94  func bswap32(in uint32) uint32 {
    95  	var buf [4]byte
    96  	binary.LittleEndian.PutUint32(buf[:], in)
    97  	return binary.BigEndian.Uint32(buf[:])
    98  }
    99  
   100  // Return an 8-byte hash for 33 to 64 bytes.
   101  func hash64Len33to64(s []byte) uint64 {
   102  	n := uint64(len(s))
   103  	mul := k2 + n*2
   104  	a := fetch64(s) * k2
   105  	b := fetch64(s[8:])
   106  	c := fetch64(s[n-24:])
   107  	d := fetch64(s[n-32:])
   108  	e := fetch64(s[16:]) * k2
   109  	f := fetch64(s[24:]) * 9
   110  	g := fetch64(s[n-8:])
   111  	h := fetch64(s[n-16:]) * mul
   112  	u := ror64(a+g, 43) + (ror64(b, 30)+c)*9
   113  	v := ((a + g) ^ d) + f + 1
   114  	w := bswap64((u+v)*mul) + h
   115  	x := ror64(e+f, 42) + c
   116  	y := (bswap64((v+w)*mul) + g) * mul
   117  	z := e + f + c
   118  	a = bswap64((x+z)*mul+y) + b
   119  	b = shiftMix((z+a)*mul+d+h) * mul
   120  	return b + x
   121  }
   122  
   123  func hash128to64(lo, hi uint64) uint64 {
   124  	// Murmur-inspired hashing.
   125  	const mul = uint64(0x9ddfea08eb382d69)
   126  	a := (lo ^ hi) * mul
   127  	a ^= a >> 47
   128  	b := (hi ^ a) * mul
   129  	b ^= b >> 47
   130  	b *= mul
   131  	return b
   132  }
   133  
   134  var fetch64 = binary.LittleEndian.Uint64 // :: []byte -> uint64
   135  var fetch32 = binary.LittleEndian.Uint32 // :: []byte -> uint32
   136  
   137  // Return a 16-byte hash for s[0] ... s[31], a, and b.  Quick and dirty.
   138  // Callers do best to use "random-looking" values for a and b.
   139  func weakHashLen32WithSeeds(s []byte, a, b uint64) (uint64, uint64) {
   140  	// Note: Was two overloads of WeakHashLen32WithSeeds.  The second is only
   141  	// ever called from the first, so I inlined it.
   142  	w := fetch64(s)
   143  	x := fetch64(s[8:])
   144  	y := fetch64(s[16:])
   145  	z := fetch64(s[24:])
   146  
   147  	a += w
   148  	b = ror64(b+a+z, 21)
   149  	c := a
   150  	a += x
   151  	a += y
   152  	b += ror64(a, 44)
   153  	return a + z, b + c
   154  }
   155  
   156  func hash32Len0to4(s []byte) uint32 {
   157  	b := uint32(0)
   158  	c := uint32(9)
   159  
   160  	for i := 0; i < len(s); i++ {
   161  		v := int8(s[i])
   162  		b = b*c1 + uint32(v)
   163  		c ^= b
   164  	}
   165  	return fmix(mur(b, mur(uint32(len(s)), c)))
   166  }
   167  
   168  func mur(a, h uint32) uint32 {
   169  	// Helper from Murmur3 for combining two 32-bit values.
   170  	a *= c1
   171  	a = ror32(a, 17)
   172  	a *= c2
   173  	h ^= a
   174  	h = ror32(h, 19)
   175  	return h*5 + 0xe6546b64
   176  }
   177  
   178  func hash32Len5to12(s []byte) uint32 {
   179  	n := uint32(len(s))
   180  	a := n
   181  	b := n * 5
   182  	c := uint32(9)
   183  	d := b
   184  
   185  	a += fetch32(s)
   186  	b += fetch32(s[n-4:])
   187  	c += fetch32(s[(n>>1)&4:])
   188  	return fmix(mur(c, mur(b, mur(a, d))))
   189  }
   190  
   191  func hash32Len13to24(s []byte) uint32 {
   192  	n := uint32(len(s))
   193  	a := fetch32(s[(n>>1)-4:])
   194  	b := fetch32(s[4:])
   195  	c := fetch32(s[n-8:])
   196  	d := fetch32(s[n>>1:])
   197  	e := fetch32(s)
   198  	f := fetch32(s[n-4:])
   199  	h := n
   200  
   201  	return fmix(mur(f, mur(e, mur(d, mur(c, mur(b, mur(a, h)))))))
   202  }
   203  
   204  // A subroutine for Hash128.  Returns a decent 128-bit hash for s based on City
   205  // and Murmur.
   206  func cityMurmur(s []byte, seed0, seed1 uint64) (lo, hi uint64) {
   207  	a := seed0
   208  	b := seed1
   209  	var c, d uint64
   210  	n := int64(len(s) - 16)
   211  	if n <= 0 {
   212  		a = shiftMix(a*k1) * k1
   213  		c = b*k1 + hash64Len0to16(s)
   214  
   215  		if len(s) >= 8 {
   216  			d = shiftMix(a + fetch64(s))
   217  		} else {
   218  			d = shiftMix(a + c)
   219  		}
   220  	} else {
   221  		c = hash64Len16(fetch64(s[len(s)-8:])+k1, a)
   222  		d = hash64Len16(b+uint64(len(s)), c+fetch64(s[len(s)-16:]))
   223  		a += d
   224  		for n > 0 {
   225  			a ^= shiftMix(fetch64(s)*k1) * k1
   226  			a *= k1
   227  			b ^= a
   228  			c ^= shiftMix(fetch64(s[8:])*k1) * k1
   229  			c *= k1
   230  			d ^= c
   231  			s = s[16:]
   232  			n -= 16
   233  		}
   234  	}
   235  	a = hash64Len16(a, c)
   236  	b = hash64Len16(d, b)
   237  	return a ^ b, hash64Len16(b, a)
   238  }