String Hash Function

December 15, 2017 ยท View on GitHub

String Hash Function

The str.hash command is defined as Murmur3_x64_128, and returns the same results on all platforms, given the same input. Murmur3 is known as a fast and high quality non-cryptographic hash function.

The str.hash command will return a list of four numbers, each 32-bit unsigned integers.

The seed parameter is optional, and defaults to 0. Changing the seed is useful for generating different hashes for the same input.

// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
// https://github.com/aappleby/smhasher

static inline uint64_t rotl64(uint64_t x, int8_t r){
  return (x << r) | (x >> (64 - r));
}

static inline uint64_t fmix64(uint64_t k){
  k ^= k >> 33;
  k *= UINT64_C(0xFF51AFD7ED558CCD);
  k ^= k >> 33;
  k *= UINT64_C(0xC4CEB9FE1A85EC53);
  k ^= k >> 33;
  return k;
}

void str_hash(const uint8_t *str, uint64_t len, uint32_t seed, uint32_t *out){
  uint64_t nblocks = len >> 4;

  uint64_t h1 = seed;
  uint64_t h2 = seed;

  uint64_t c1 = UINT64_C(0x87C37B91114253D5);
  uint64_t c2 = UINT64_C(0x4CF5AD432745937F);

  for (uint64_t i = 0; i < nblocks; i++){
    uint64_t ki = i * 16;
    uint64_t k1 =
      ((uint64_t)str[ki +  0]      ) |
      ((uint64_t)str[ki +  1] <<  8) |
      ((uint64_t)str[ki +  2] << 16) |
      ((uint64_t)str[ki +  3] << 24) |
      ((uint64_t)str[ki +  4] << 32) |
      ((uint64_t)str[ki +  5] << 40) |
      ((uint64_t)str[ki +  6] << 48) |
      ((uint64_t)str[ki +  7] << 56);
    uint64_t k2 =
      ((uint64_t)str[ki +  8]      ) |
      ((uint64_t)str[ki +  9] <<  8) |
      ((uint64_t)str[ki + 10] << 16) |
      ((uint64_t)str[ki + 11] << 24) |
      ((uint64_t)str[ki + 12] << 32) |
      ((uint64_t)str[ki + 13] << 40) |
      ((uint64_t)str[ki + 14] << 48) |
      ((uint64_t)str[ki + 15] << 56);

    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;
  }

  const uint8_t *tail = &str[nblocks << 4];

  uint64_t k1 = 0;
  uint64_t k2 = 0;

  switch(len & 15) {
    case 15: k2 ^= (uint64_t)(tail[14]) << 48;
    case 14: k2 ^= (uint64_t)(tail[13]) << 40;
    case 13: k2 ^= (uint64_t)(tail[12]) << 32;
    case 12: k2 ^= (uint64_t)(tail[11]) << 24;
    case 11: k2 ^= (uint64_t)(tail[10]) << 16;
    case 10: k2 ^= (uint64_t)(tail[ 9]) << 8;
    case  9: k2 ^= (uint64_t)(tail[ 8]) << 0;

    k2 *= c2;
    k2 = rotl64(k2, 33);
    k2 *= c1;
    h2 ^= k2;

    case  8: k1 ^= (uint64_t)(tail[ 7]) << 56;
    case  7: k1 ^= (uint64_t)(tail[ 6]) << 48;
    case  6: k1 ^= (uint64_t)(tail[ 5]) << 40;
    case  5: k1 ^= (uint64_t)(tail[ 4]) << 32;
    case  4: k1 ^= (uint64_t)(tail[ 3]) << 24;
    case  3: k1 ^= (uint64_t)(tail[ 2]) << 16;
    case  2: k1 ^= (uint64_t)(tail[ 1]) << 8;
    case  1: k1 ^= (uint64_t)(tail[ 0]) << 0;

    k1 *= c1;
    k1 = rotl64(k1, 31);
    k1 *= c2;
    h1 ^= k1;
  }

  h1 ^= len;
  h2 ^= len;

  h1 += h2;
  h2 += h1;

  h1 = fmix64(h1);
  h2 = fmix64(h2);

  h1 += h2;
  h2 += h1;

  out[0] = h1 & 0xFFFFFFFF;
  out[1] = h1 >> 32;
  out[2] = h2 & 0xFFFFFFFF;
  out[3] = h2 >> 32;
}
str.hash 'hello, world', 123   # => {3439238593,  804096095, 2029097957, 3684287146}
str.hash 'demon produce aisle' # => {2133076460, 2322631415, 1728380306, 2686374473}