5 #ifndef CVMFS_SMALLHASH_H_
6 #define CVMFS_SMALLHASH_H_
8 #ifndef __STDC_FORMAT_MACROS
10 #define __STDC_FORMAT_MACROS
13 #include <gtest/gtest_prod.h>
33 template<
class Key,
class Value,
class Derived>
58 void Init(uint32_t expected_size, Key empty,
59 uint32_t (*hasher)(
const Key &key)) {
62 capacity_ =
static_cast<uint32_t
>(
static_cast<double>(expected_size)
70 bool Lookup(
const Key &key, Value *value)
const {
73 const bool found =
DoLookup(key, &bucket, &collisions);
89 if (
keys_[bucket] == *key) {
102 const bool found =
DoLookup(key, &bucket, &collisions);
106 void Insert(
const Key &key,
const Value &value) {
107 static_cast<Derived *
>(
this)->
Grow();
108 const bool overwritten =
DoInsert(key, value,
true);
109 size_ += !overwritten;
115 const bool found =
DoLookup(key, &bucket, &collisions);
121 Key rehash =
keys_[bucket];
126 static_cast<Derived *
>(
this)->
Shrink();
135 const double unit =
sizeof(Key) +
sizeof(Value);
140 uint32_t *max_collisions)
const {
156 double bucket = (
static_cast<double>(
hasher_(key))
158 /
static_cast<double>(
static_cast<uint32_t
>(-1)));
159 return static_cast<uint32_t
>(bucket) %
capacity_;
165 for (uint32_t i = 0; i <
capacity_; ++i) {
166 new (
keys_ + i) Key();
168 for (uint32_t i = 0; i <
capacity_; ++i) {
175 for (uint32_t i = 0; i < c; ++i) {
178 for (uint32_t i = 0; i < c; ++i) {
191 const bool count_collisions) {
194 const bool overwritten =
DoLookup(key, &bucket, &collisions);
195 if (count_collisions) {
204 bool DoLookup(
const Key &key, uint32_t *bucket, uint32_t *collisions)
const {
208 if (
keys_[*bucket] == key)
244 template<
class Key,
class Value>
246 :
public SmallHashBase<Key, Value, SmallHashFixed<Key, Value> > {
258 template<
class Key,
class Value>
260 :
public SmallHashBase<Key, Value, SmallHashDynamic<Key, Value> > {
309 uint32_t target_capacity =
capacity() / 2;
326 uint32_t *shuffled =
static_cast<uint32_t *
>(smmap(N *
sizeof(uint32_t)));
328 for (
unsigned i = 0; i < N; ++i)
331 for (
unsigned i = 0; i < N - 1; ++i) {
332 const uint32_t swap_idx = i +
g_prng.
Next(N - i);
333 uint32_t tmp = shuffled[i];
334 shuffled[i] = shuffled[swap_idx];
335 shuffled[swap_idx] = tmp;
344 uint32_t old_size =
size();
350 if (new_capacity < old_capacity) {
352 for (uint32_t i = 0; i < old_capacity; ++i) {
355 old_values[shuffled_indices[i]]);
358 smunmap(shuffled_indices);
360 for (uint32_t i = 0; i < old_capacity; ++i) {
373 for (uint32_t i = 0; i < other.
capacity_; ++i) {
376 other.
values_[shuffled_indices[i]]);
379 smunmap(shuffled_indices);
393 template<
class Key,
class Value>
403 uint32_t (*hasher)(
const Key &key)) {
408 locks_ =
static_cast<pthread_mutex_t *
>(
409 smalloc(N *
sizeof(pthread_mutex_t)));
410 for (uint8_t i = 0; i < N; ++i) {
411 int retval = pthread_mutex_init(&locks_[i], NULL);
413 hashmaps_[i].Init(128, empty_key, hasher);
419 pthread_mutex_destroy(&
locks_[i]);
425 bool Lookup(
const Key &key, Value *value) {
428 const bool result =
hashmaps_[target].Lookup(key, value);
433 void Insert(
const Key &key,
const Value &value) {
469 hashmaps_[i].GetCollisionStats(&num_collisions[i], &max_collisions[i]);
476 uint32_t hash =
MurmurHash2(&key,
sizeof(key), 0x37);
477 double bucket =
static_cast<double>(hash)
479 /
static_cast<double>(
static_cast<uint32_t
>(-1));
483 inline void Lock(
const uint8_t target) {
484 int retval = pthread_mutex_lock(&
locks_[target]);
488 inline void Unlock(
const uint8_t target) {
489 int retval = pthread_mutex_unlock(&
locks_[target]);
500 template<
class Key,
class Value>
503 template<
class Key,
class Value,
class Derived>
506 template<
class Key,
class Value>
509 template<
class Key,
class Value>
512 #endif // CVMFS_SMALLHASH_H_
uint32_t capacity() const
SmallHashDynamic< Key, Value > * hashmaps_
void GetCollisionStats(uint64_t *num_collisions, uint32_t *max_collisions) const
uint32_t(* hasher_)(const Key &key)
void Unlock(const uint8_t target)
void Init(const uint8_t num_hashmaps, const Key &empty_key, uint32_t(*hasher)(const Key &key))
void Insert(const Key &key, const Value &value)
void GetSizes(uint32_t *sizes)
uint64_t bytes_allocated() const
uint32_t ScaleHash(const Key &key) const
assert((mem||(size==0))&&"Out Of Memory")
uint32_t * ShuffleIndices(const uint32_t N)
void CopyFrom(const SmallHashDynamic< Key, Value > &other)
static const double kThresholdGrow
bool Lookup(const Key &key, Value *value)
uint8_t SelectHashmap(const Key &key)
void Lock(const uint8_t target)
SmallHashDynamic(const SmallHashDynamic< Key, Value > &other)
uint32_t threshold_shrink_
bool LookupEx(Key *key, Value *value) const
static double GetEntrySize()
uint32_t capacity() const
void Insert(const Key &key, const Value &value)
static const double kThresholdGrow
uint64_t bytes_allocated_
void Erase(const Key &key)
static const double kLoadFactor
bool Contains(const Key &key) const
bool DoInsert(const Key &key, const Value &value, const bool count_collisions)
void DoClear(const bool reset_capacity)
void GetCollisionStats(uint64_t *num_collisions, uint32_t *max_collisions)
void SetHasher(uint32_t(*hasher)(const Key &key))
uint32_t num_migrates() const
bool Erase(const Key &key)
uint8_t num_hashmaps() const
FRIEND_TEST(T_Smallhash, InsertAndCopyMd5Slow)
uint32_t initial_capacity_
static const double kThresholdShrink
bool Lookup(const Key &key, Value *value) const
void DeallocMemory(Key *k, Value *v, uint32_t c)
void Init(uint32_t expected_size, Key empty, uint32_t(*hasher)(const Key &key))
bool DoLookup(const Key &key, uint32_t *bucket, uint32_t *collisions) const
SmallHashBase< Key, Value, SmallHashDynamic< Key, Value > > Base
void Migrate(const uint32_t new_capacity)
uint32_t MurmurHash2(const void *key, int len, uint32_t seed)
static const double kThresholdShrink
SmallHashDynamic< Key, Value > & operator=(const SmallHashDynamic< Key, Value > &other)
uint32_t Next(const uint64_t boundary)