998951619f
We did not intend to imply our copyrights last forever Closes #759
136 linhas
4.2 KiB
C++
136 linhas
4.2 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
|
| Copyright (c) 1997-2010 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
/**
|
|
* FurcHash -- a consistent hash function using a binary decision tree.
|
|
* Based on an algorithm by Mark Rabkin with two changes:
|
|
* 1) Uses MurmurHash64A to hash the original key and to generate
|
|
* additional bits by recursively rehashing.
|
|
* 2) The original recursive algorithm for the decision tree has been
|
|
* made iterative.
|
|
*
|
|
* Assumes that |m| is 8 million or less (2^FURC_SHIFT). Making FURC_SHIFT
|
|
* bigger also makes furc_hash modestly slower.
|
|
*
|
|
* Performance is in the sub-500ns range to over 100,000 shards with 13-byte
|
|
* keys. This version of furc_hash is fairly insensitive to key length since
|
|
* additional bits are generated by re-hashing the initial MurmurHash64A.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <sys/time.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "hphp/runtime/ext/hash/hash_murmur.h"
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
/* Maximum number tries for in-range result before just returning 0. */
|
|
#define MAX_TRIES 32
|
|
|
|
/* Gap in bit index per try; limits us to 2^FURC_SHIFT shards. Making this
|
|
* larger will sacrifice a modest amount of performance.
|
|
*/
|
|
#define FURC_SHIFT 23
|
|
|
|
/* Size of cache for hash values; should be > MAXTRIES * (FURCSHIFT + 1) */
|
|
#define FURC_CACHE_SIZE 1024
|
|
|
|
|
|
/**
|
|
* furc_get_bit -- the bitstream generator
|
|
*
|
|
* Given a key and an index, provides a pseudorandom bit dependent on both.
|
|
* Caches hash values; a NULL key clears the cache.
|
|
*/
|
|
static uint32_t furc_get_bit(const char* const key, const size_t len,
|
|
const uint32_t idx, uint64_t* hash,
|
|
int32_t* old_ord_p) {
|
|
int32_t ord = (idx >> 6);
|
|
int n;
|
|
|
|
if (key == NULL) {
|
|
*old_ord_p = -1;
|
|
return 0;
|
|
}
|
|
|
|
if (*old_ord_p < ord) {
|
|
for (n = *old_ord_p + 1; n <= ord; n++) {
|
|
hash[n] = ((n == 0)
|
|
? murmur_hash_64A(key, len, SEED)
|
|
: murmur_rehash_64A(hash[n-1]));
|
|
}
|
|
*old_ord_p = ord;
|
|
}
|
|
|
|
return (hash[ord] >> (idx&0x3f)) & 0x1;
|
|
}
|
|
|
|
inline uint32_t furc_maximum_pool_size(void) {
|
|
return (1 << FURC_SHIFT);
|
|
}
|
|
|
|
uint32_t furc_hash_internal(const char* const key, const size_t len, const uint32_t m) {
|
|
uint32_t tries;
|
|
uint32_t d;
|
|
uint32_t num;
|
|
uint32_t i;
|
|
uint32_t a;
|
|
uint64_t hash[FURC_CACHE_SIZE];
|
|
int32_t old_ord;
|
|
|
|
assert(m <= furc_maximum_pool_size());
|
|
|
|
if (m <= 1) {
|
|
return 0;
|
|
}
|
|
|
|
furc_get_bit(NULL, 0, 0, hash, &old_ord);
|
|
for (d = 0; m > (1ul << d); d++)
|
|
;
|
|
|
|
a = d;
|
|
for (tries = 0; tries < MAX_TRIES; tries++) {
|
|
while (!furc_get_bit(key, len, a, hash, &old_ord)) {
|
|
if (--d == 0) {
|
|
return 0;
|
|
}
|
|
a = d;
|
|
}
|
|
a += FURC_SHIFT;
|
|
num = 1;
|
|
for (i = 0; i < d-1; i++) {
|
|
num = (num << 1) | furc_get_bit(key, len, a, hash, &old_ord);
|
|
a += FURC_SHIFT;
|
|
}
|
|
if (num < m) {
|
|
return num;
|
|
}
|
|
}
|
|
|
|
// Give up; return 0, which is a legal value in all cases.
|
|
return 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|