Created
June 17, 2012 03:23
-
-
Save kumagi/2943289 to your computer and use it in GitHub Desktop.
single thread Hopscotch hashing with C++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef MAP_HPP | |
#define MAP_HPP | |
//#include <cstdint> | |
#include <stdint.h> | |
#include <cstddef> | |
#include <map> | |
#include <functional> | |
#include <boost/functional/hash.hpp> | |
#include <memory> | |
#include <utility> | |
#include <assert.h> | |
#include <iostream> | |
namespace nanahan{ | |
using boost::hash; | |
//using std::hash; | |
namespace detail{ | |
size_t bitcount(uint32_t bits) { | |
bits = (bits & 0x55555555LU) + (bits >> 1 & 0x55555555LU); | |
bits = (bits & 0x33333333LU) + (bits >> 2 & 0x33333333LU); | |
bits = (bits & 0x0f0f0f0fLU) + (bits >> 4 & 0x0f0f0f0fLU); | |
bits = (bits & 0x00ff00ffLU) + (bits >> 8 & 0x00ff00ffLU); | |
return (bits & 0x0000ffffLU) + (bits >>16 & 0x0000ffffLU); | |
} | |
size_t bitcount(uint64_t bits) { | |
bits = (bits & 0x5555555555555555LLU) + (bits >> 1 & 0x5555555555555555LLU); | |
bits = (bits & 0x3333333333333333LLU) + (bits >> 2 & 0x3333333333333333LLU); | |
bits = (bits & 0x0f0f0f0f0f0f0f0fLLU) + (bits >> 4 & 0x0f0f0f0f0f0f0f0fLLU); | |
bits = (bits & 0x00ff00ff00ff00ffLLU) + (bits >> 8 & 0x00ff00ff00ff00ffLLU); | |
bits = (bits & 0x0000ffff0000ffffLLU) + (bits >>16 & 0x0000ffff0000ffffLLU); | |
return (bits & 0x00000000ffffffffLLU) + (bits >>32 & 0x00000000ffffffffLLU); | |
} | |
} | |
//#define nanahan_64bit | |
#ifdef nanahan_64bit | |
# define slot_size uint64_t | |
# define one 1LLU | |
#else | |
# define slot_size uint32_t | |
# define one 1LU | |
#endif | |
template<typename Key, | |
typename Value, | |
typename Hash = hash<Key>, | |
typename Pred = std::equal_to<Key>, | |
typename Alloc = std::allocator<std::pair<const Key, Value> > | |
> | |
class Map{ | |
private: | |
typedef Map<Key,Value,Hash,Pred,Alloc> ThisMap; | |
typedef slot_size Slot; | |
static const uint64_t INITIAL_SIZE = 8; | |
static const uint32_t SLOTSIZE = sizeof(Slot) * 8; | |
static const uint32_t HOP_RANGE = SLOTSIZE * 8; | |
typedef typename std::pair<const Key, Value> Kvp; | |
struct bucket{ | |
Slot slot_; | |
std::pair<const Key, Value> *kvp_; | |
bucket() | |
:slot_(0), kvp_(NULL){} | |
void dump()const{ | |
std::cout << "slot[" << slot_ << "] "; | |
if(kvp_ == NULL) { | |
std::cout << "<empty>"; | |
}else{ | |
std::cout << "(" << kvp_->first << "=>" << kvp_->second << ")"; | |
} | |
} | |
}; | |
static typename Alloc::template rebind<bucket>::other bucket_alloc; | |
static Alloc alloc; | |
public: | |
class const_iterator; | |
class iterator{ | |
public: | |
iterator(bucket* b, bucket* buckets, size_t size) | |
:it_(b), buckets_(buckets),size_(size){} | |
const std::pair<const Key, Value>* operator->()const{return it_->kvp_;} | |
std::pair<const Key, Value>* operator->(){return it_->kvp_;} | |
//iterator(const iterator&) = default; | |
//iterator(iterator&& i):it_(i.it_),buckets_(i.buckets_),size_(i.size_){} | |
bool operator==(const iterator& rhs)const{ return it_ == rhs.it_; } | |
bool operator!=(const iterator& rhs)const{ return !operator==(rhs); } | |
const std::pair<const Key, Value>& operator*()const{ return *it_->kvp_; } | |
std::pair<const Key, Value>& operator*(){ return *it_->kvp_; } | |
bool is_end()const{ return buckets_ + size_ == it_; } | |
iterator operator++(){ | |
do{ | |
++it_; | |
}while(it_ != &buckets_[size_] && it_->kvp_ == NULL); | |
return *this; | |
} | |
iterator operator--(){ | |
do{ | |
--it_; | |
}while(it_ != &buckets_[0] && it_->kvp_ == NULL); | |
return *this; | |
} | |
void dump()const{ | |
it_->dump(); | |
} | |
friend class const_iterator; | |
private: | |
void remove_unsafe(){ | |
it_->kvp_ = NULL; | |
} | |
iterator(); | |
bucket* it_; | |
const bucket* const buckets_; | |
const size_t size_; | |
friend class nanahan::Map<Key,Value,Hash,Pred,Alloc>; | |
}; | |
class const_iterator{ | |
public: | |
const_iterator(bucket* b, bucket* buckets, size_t size) | |
:it_(b), buckets_(buckets),size_(size){} | |
const std::pair<const Key, Value>* operator->()const{return it_->kvp_;} | |
//const_iterator(const const_iterator&) = default; | |
const_iterator(const iterator& i):it_(i.it_),buckets_(i.buckets_),size_(i.size_){} | |
//const_iterator(iterator&& i):it_(i.it_),buckets_(i.buckets_),size_(i.size_){} | |
bool operator==(const const_iterator& rhs)const{ return it_ == rhs.it_; } | |
bool operator!=(const const_iterator& rhs)const{ return !operator==(rhs); } | |
const std::pair<const Key, Value>& operator*()const{ return *it_->kvp_; } | |
const_iterator operator++(){ | |
do{ | |
++it_; | |
}while(it_ != &buckets_[size_] && it_->kvp_ == NULL); | |
return *this; | |
} | |
const_iterator operator--(){ | |
do{ | |
--it_; | |
}while(it_ != &buckets_[0] && it_->kvp_ == NULL); | |
return *this; | |
} | |
void dump()const{ | |
it_->dump(); | |
} | |
private: | |
const_iterator(); | |
const bucket* it_; | |
const bucket* const buckets_; | |
const size_t size_; | |
friend class nanahan::Map<Key,Value,Hash,Pred,Alloc>; | |
}; | |
Map(size_t initial_size = 8) | |
:bucket_size_(initial_size), | |
buckets_(new bucket[bucket_size_]), | |
used_size_(0), | |
extending_(false) | |
{} | |
Map(const Map& orig) | |
:bucket_size_(orig.bucket_size_), | |
buckets_(new bucket[bucket_size_]), | |
used_size_(0), | |
extending_(false) | |
{ | |
const_iterator it = orig.begin(); | |
for(; it != orig.end(); ++it){ | |
insert(*it); | |
} | |
} | |
Map& operator=(const Map& orig){ | |
for(size_t i = 0; i < bucket_size_; ++i){ | |
if(buckets_[i].kvp_ != NULL){ | |
buckets_[i].kvp_->~Kvp(); | |
allocator_.deallocate(buckets_[i].kvp_, 1); | |
buckets_[i].kvp_ = NULL; | |
} | |
buckets_[i].slot_ = 0; | |
} | |
used_size_ = 0; | |
if(bucket_size_ != orig.bucket_size_){ | |
delete buckets_; | |
buckets_ = new bucket[orig.bucket_size_]; | |
} | |
Map::const_iterator it = orig.begin(); | |
for(; it != orig.end(); ++it){ | |
insert(*it); | |
} | |
return *this; | |
} | |
/* insert with std::pair */ | |
inline std::pair<iterator, bool> insert(const std::pair<const Key, Value>& kvp) | |
{ | |
const size_t hashvalue = Hash()(kvp.first); | |
const size_t target_slot = locate(hashvalue, 0); | |
//std::cout << "target slot:" << target_slot << std::endl; | |
bucket* target_bucket = buckets_ + target_slot; | |
iterator searched = find(kvp.first, hashvalue); | |
if(!searched.is_end()){ | |
return std::make_pair(searched, false); | |
} | |
//std::cout << "insert: " << kvp.first << std::endl; | |
/* search empty bucket */ | |
bucket* empty_bucket = target_bucket; | |
size_t distance = 0; | |
size_t index = target_slot; | |
const size_t max_distance = (HOP_RANGE < bucket_size_ ? | |
HOP_RANGE : bucket_size_); | |
do{ | |
if(empty_bucket->kvp_ == NULL) break; | |
index = (index + 1) & (bucket_size_ - 1); | |
empty_bucket = &buckets_[index]; | |
++distance; | |
}while(distance < max_distance); | |
if(max_distance <= distance){ | |
bucket_extend(); | |
return insert(kvp); | |
} | |
/* move empty bucket if it was too far */ | |
while(empty_bucket != NULL && SLOTSIZE <= distance){ | |
/* | |
std::cout << "find closer bucket:" << std::endl; | |
std::cout << "distance : " << distance << " => "; | |
//*/ | |
find_closer_bucket(&empty_bucket, &distance); | |
//std::cout << distance << std::endl; | |
} | |
if(empty_bucket == NULL){ | |
bucket_extend(); | |
return insert(kvp); | |
} | |
assert(empty_bucket->kvp_ == NULL); | |
empty_bucket->kvp_ = allocator_.allocate(1); | |
allocator_.construct(empty_bucket->kvp_, kvp); | |
//std::cout << "dist:" << distance << std::endl; | |
target_bucket->slot_ |= one << distance; | |
++used_size_; | |
//dump(); | |
return std::make_pair(iterator(empty_bucket, buckets_, bucket_size_), | |
true); | |
} | |
void bucket_extend(){ | |
const size_t old_size = bucket_size_; | |
size_t new_size = old_size * 2; | |
/* | |
std::cout << "resize from " << used_size_ << " " | |
<< old_size << " -> " << new_size << std::endl; | |
std::cout << "extending dencity: " << | |
used_size_ << " / " << bucket_size_ << "\t| " << | |
static_cast<double>(used_size_) * 100 / bucket_size_ << " %" << std::endl; | |
//*/ | |
while(true){ | |
Map new_map(new_size); | |
bool ok = true; | |
iterator end_it = end(); | |
for(iterator it = begin(); it != end_it; ++it){ | |
const bool result = new_map.insert_unsafe(&*it); | |
if(!result){ // failed to insert | |
std::cout << "failed to insert_unsafe()" << std::endl; | |
ok = false; | |
break; | |
} | |
} | |
if(!ok){ | |
new_size *= 2; | |
new_map.clear_unsafe(); | |
continue; | |
}else{ | |
/* | |
std::cout << "old map" << std::endl; | |
dump(); | |
std::cout << "to move map" << std::endl; | |
new_map.dump(); | |
//*/ | |
std::swap(buckets_, new_map.buckets_); | |
bucket_size_ = new_size; | |
std::swap(allocator_, new_map.allocator_); | |
new_map.clear_unsafe(); | |
/* | |
std::cout << "new map" << std::endl; | |
dump(); | |
std::cout << "deleting map" << std::endl; | |
new_map.dump(); | |
*/ | |
break; | |
} | |
} | |
} | |
/* erase the data*/ | |
iterator erase(iterator where) | |
{ | |
bucket* const target = where.it_; | |
bucket* start_bucket = target; | |
if(where.is_end()){ | |
//std::cout << "erase: end() passed"<< std::endl; | |
return end(); | |
} | |
if(find(where->first).is_end()){return end();} | |
//std::cout << "erase!" << slot->kvp_->first << std::endl; | |
allocator_.destroy(target->kvp_); | |
allocator_.deallocate(target->kvp_, 1); | |
target->kvp_ = NULL; | |
for(Slot i = 1; i != 0; i <<= 1){ | |
if((start_bucket->slot_ & i) != 0){ | |
--used_size_; | |
start_bucket->slot_ &= ~i; | |
return iterator(target , buckets_, bucket_size_); | |
} | |
--start_bucket; | |
} | |
//std::cout << "erased:" << std::endl; | |
return end(); | |
} | |
iterator find(const Key& key) | |
{ | |
return find(key, Hash()(key)); | |
} | |
iterator find(const Key& key, const size_t hashvalue) | |
{ | |
const unsigned int target = locate(hashvalue, 0); | |
bucket *target_bucket = buckets_ + target; | |
Slot slot_info = target_bucket->slot_; | |
/* | |
std::cout << "search:[" << target << "] for " << key <<std::endl; | |
//*/ | |
Pred pred; // comperator | |
while(slot_info){ | |
/* | |
std::cout << "trying " << slot_info << " "; | |
target_bucket->dump(); | |
std::cout << std::endl; | |
//*/ | |
if((slot_info & 1)){ | |
assert(target_bucket); | |
if(pred(target_bucket->kvp_->first, key)){ | |
return iterator(target_bucket, buckets_, bucket_size_); | |
} | |
slot_info &= ~1; | |
} | |
const size_t gap = detail::bitcount((~slot_info) & (slot_info - 1)); | |
slot_info >>= gap; | |
//std::cout << "new slot_info:" << slot_info << " gap:" << gap << std::endl; | |
target_bucket= forward(target_bucket, gap); | |
} | |
//std::cout << "not found" << std::endl; | |
return end(); | |
} | |
inline void find_closer_bucket(bucket** free_bucket, size_t* distance) | |
{ | |
bucket* move_bucket = *free_bucket - SLOTSIZE + 1; | |
if(move_bucket < buckets_){ | |
move_bucket += bucket_size_; | |
} | |
for(size_t i = SLOTSIZE - 1; 0 < i; --i, move_bucket = next(move_bucket)){ | |
for(size_t j = 0; j <= i; ++j){ | |
if(move_bucket->slot_ & (one << j)){ | |
(*free_bucket)->kvp_ = bucket_index(move_bucket, j).kvp_; | |
/* | |
std::cout << "moving from " << j << " to " << i << "bucket"; | |
bucket_index(move_bucket, j).dump(); | |
std::cout << std::endl; | |
//*/ | |
bucket_index(move_bucket,j).kvp_ = NULL; | |
move_bucket->slot_ &= ~(one << j); | |
move_bucket->slot_ |= one << i; | |
*free_bucket = &bucket_index(move_bucket,j); | |
*distance -= i - j; | |
return; | |
} | |
} | |
} | |
*free_bucket = NULL; | |
*distance = 0; | |
} | |
const_iterator begin()const{ | |
bucket* head = buckets_; | |
while(head->kvp_ == NULL && head != &buckets_[bucket_size_]){++head;} | |
return iterator(head, buckets_, bucket_size_); | |
} | |
const_iterator end()const{ | |
return iterator(&buckets_[bucket_size_], buckets_, bucket_size_); | |
} | |
iterator begin(){ | |
bucket* head = buckets_; | |
while(head->kvp_ == NULL && head != &buckets_[bucket_size_]){++head;} | |
return iterator(head, buckets_, bucket_size_); | |
} | |
iterator end(){ | |
return iterator(&buckets_[bucket_size_], buckets_, bucket_size_); | |
} | |
size_t size()const{return used_size_;} | |
bool empty()const{return used_size_ == 0;} | |
void dump()const{ | |
for(size_t i=0; i < bucket_size_; ++i){ | |
std::cout << "[" << i << "] "; | |
buckets_[i].dump(); | |
std::cout << std::endl; | |
} | |
std::cout << "total:" << used_size_ << std::endl; | |
} | |
void clear(){ | |
for(size_t i = 0; i < bucket_size_; ++i){ | |
if(buckets_[i].kvp_ != NULL){ | |
buckets_[i].kvp_->~Kvp(); | |
allocator_.deallocate(buckets_[i].kvp_, 1); | |
} | |
buckets_[i].slot_ = 0; | |
} | |
delete[] buckets_; | |
buckets_ = new bucket[INITIAL_SIZE]; | |
bucket_size_ = INITIAL_SIZE; | |
used_size_ = 0; | |
} | |
~Map(){ | |
//dump(); | |
if(!extending_){ | |
clear(); | |
} | |
delete[] buckets_; | |
} | |
private: | |
inline void clear_unsafe(){ | |
extending_ = true; | |
return; | |
} | |
inline bool insert_unsafe(std::pair<const Key, Value>* const kvp) | |
{ | |
const size_t hashvalue = Hash()(kvp->first); | |
const size_t target_slot = hashvalue & (bucket_size_ - 1); | |
bucket* target_bucket = buckets_+ target_slot; | |
//std::cout << "insert: " << kvp.first << std::endl; | |
/* search empty bucket */ | |
bucket* empty_bucket = target_bucket; | |
size_t distance = 0; | |
size_t index = target_slot; | |
const size_t max_distance = (HOP_RANGE < bucket_size_ ? | |
HOP_RANGE : bucket_size_); | |
do{ | |
if(empty_bucket->kvp_ == NULL) break; | |
index = (index + 1) & (bucket_size_ - 1); | |
empty_bucket = buckets_ + index; | |
++distance; | |
}while(distance < max_distance); | |
if(max_distance <= distance){ | |
return false; | |
} | |
/* move empty bucket if it was too far */ | |
while(empty_bucket != NULL && SLOTSIZE <= distance){ | |
find_closer_bucket(&empty_bucket, &distance); | |
} | |
if(empty_bucket == NULL){ | |
return false; | |
} | |
assert(empty_bucket->kvp_ == NULL); | |
empty_bucket->kvp_ = kvp; | |
target_bucket->slot_ |= one << distance; | |
return true; | |
} | |
inline size_t locate(size_t start, size_t diff)const{ | |
return (start + diff) & (bucket_size_ - 1); | |
} | |
inline bucket* next(bucket* b){ | |
return forward(b, 1); | |
} | |
inline bucket* forward(bucket* b, size_t stride)const{ | |
return (b + stride) < &buckets_[bucket_size_] ? | |
b + stride : b + stride - bucket_size_; | |
} | |
inline bucket& bucket_index(bucket* start, size_t index){ | |
bucket* target = start + index; | |
return target < &buckets_[bucket_size_] ? *target : *(target - bucket_size_); | |
} | |
uint64_t bucket_size_; | |
bucket* buckets_; | |
size_t used_size_; | |
Alloc allocator_; | |
bool extending_; | |
}; | |
} // namespace nanahan | |
#undef nanahan_64bit | |
#undef one | |
#undef slot_size | |
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef MAP_HPP | |
#define MAP_HPP | |
//#include <cstdint> | |
#include <stdint.h> | |
#include <cstddef> | |
#include <map> | |
#include <functional> | |
#include <boost/functional/hash.hpp> | |
#include <memory> | |
#include <utility> | |
#include <assert.h> | |
#include <iostream> | |
namespace nanahan{ | |
using boost::hash; | |
//using std::hash; | |
namespace detail{ | |
size_t bitcount(uint32_t bits) { | |
bits = (bits & 0x55555555LU) + (bits >> 1 & 0x55555555LU); | |
bits = (bits & 0x33333333LU) + (bits >> 2 & 0x33333333LU); | |
bits = (bits & 0x0f0f0f0fLU) + (bits >> 4 & 0x0f0f0f0fLU); | |
bits = (bits & 0x00ff00ffLU) + (bits >> 8 & 0x00ff00ffLU); | |
return (bits & 0x0000ffffLU) + (bits >>16 & 0x0000ffffLU); | |
} | |
size_t bitcount(uint64_t bits) { | |
bits = (bits & 0x5555555555555555LLU) + (bits >> 1 & 0x5555555555555555LLU); | |
bits = (bits & 0x3333333333333333LLU) + (bits >> 2 & 0x3333333333333333LLU); | |
bits = (bits & 0x0f0f0f0f0f0f0f0fLLU) + (bits >> 4 & 0x0f0f0f0f0f0f0f0fLLU); | |
bits = (bits & 0x00ff00ff00ff00ffLLU) + (bits >> 8 & 0x00ff00ff00ff00ffLLU); | |
bits = (bits & 0x0000ffff0000ffffLLU) + (bits >>16 & 0x0000ffff0000ffffLLU); | |
return (bits & 0x00000000ffffffffLLU) + (bits >>32 & 0x00000000ffffffffLLU); | |
} | |
} | |
//#define nanahan_64bit | |
#ifdef nanahan_64bit | |
# define slot_size uint64_t | |
# define one 1LLU | |
#else | |
# define slot_size uint32_t | |
# define one 1LU | |
#endif | |
template<typename Key, | |
typename Value, | |
typename Hash = hash<Key>, | |
typename Pred = std::equal_to<Key>, | |
typename Alloc = std::allocator<std::pair<const Key, Value> > | |
> | |
class Map{ | |
private: | |
typedef Map<Key,Value,Hash,Pred,Alloc> ThisMap; | |
typedef slot_size Slot; | |
static const uint64_t INITIAL_SIZE = 8; | |
static const uint32_t SLOTSIZE = sizeof(Slot) * 8; | |
static const uint32_t HOP_RANGE = SLOTSIZE * 8; | |
typedef typename std::pair<const Key, Value> Kvp; | |
struct bucket{ | |
Slot slot_; | |
std::pair<const Key, Value> *kvp_; | |
bucket() | |
:slot_(0), kvp_(NULL){} | |
void dump()const{ | |
std::cout << "slot[" << slot_ << "] "; | |
if(kvp_ == NULL) { | |
std::cout << "<empty>"; | |
}else{ | |
std::cout << "(" << kvp_->first << "=>" << kvp_->second << ")"; | |
} | |
} | |
}; | |
static typename Alloc::template rebind<bucket>::other bucket_alloc; | |
static Alloc alloc; | |
public: | |
class const_iterator; | |
class iterator{ | |
public: | |
iterator(bucket* b, bucket* buckets, size_t size) | |
:it_(b), buckets_(buckets),size_(size){} | |
const std::pair<const Key, Value>* operator->()const{return it_->kvp_;} | |
std::pair<const Key, Value>* operator->(){return it_->kvp_;} | |
//iterator(const iterator&) = default; | |
//iterator(iterator&& i):it_(i.it_),buckets_(i.buckets_),size_(i.size_){} | |
bool operator==(const iterator& rhs)const{ return it_ == rhs.it_; } | |
bool operator!=(const iterator& rhs)const{ return !operator==(rhs); } | |
const std::pair<const Key, Value>& operator*()const{ return *it_->kvp_; } | |
std::pair<const Key, Value>& operator*(){ return *it_->kvp_; } | |
bool is_end()const{ return buckets_ + size_ == it_; } | |
iterator operator++(){ | |
do{ | |
++it_; | |
}while(it_ != &buckets_[size_] && it_->kvp_ == NULL); | |
return *this; | |
} | |
iterator operator--(){ | |
do{ | |
--it_; | |
}while(it_ != &buckets_[0] && it_->kvp_ == NULL); | |
return *this; | |
} | |
void dump()const{ | |
it_->dump(); | |
} | |
friend class const_iterator; | |
private: | |
void remove_unsafe(){ | |
it_->kvp_ = NULL; | |
} | |
iterator(); | |
bucket* it_; | |
const bucket* const buckets_; | |
const size_t size_; | |
friend class nanahan::Map<Key,Value,Hash,Pred,Alloc>; | |
}; | |
class const_iterator{ | |
public: | |
const_iterator(bucket* b, bucket* buckets, size_t size) | |
:it_(b), buckets_(buckets),size_(size){} | |
const std::pair<const Key, Value>* operator->()const{return it_->kvp_;} | |
//const_iterator(const const_iterator&) = default; | |
const_iterator(const iterator& i):it_(i.it_),buckets_(i.buckets_),size_(i.size_){} | |
//const_iterator(iterator&& i):it_(i.it_),buckets_(i.buckets_),size_(i.size_){} | |
bool operator==(const const_iterator& rhs)const{ return it_ == rhs.it_; } | |
bool operator!=(const const_iterator& rhs)const{ return !operator==(rhs); } | |
const std::pair<const Key, Value>& operator*()const{ return *it_->kvp_; } | |
const_iterator operator++(){ | |
do{ | |
++it_; | |
}while(it_ != &buckets_[size_] && it_->kvp_ == NULL); | |
return *this; | |
} | |
const_iterator operator--(){ | |
do{ | |
--it_; | |
}while(it_ != &buckets_[0] && it_->kvp_ == NULL); | |
return *this; | |
} | |
void dump()const{ | |
it_->dump(); | |
} | |
private: | |
const_iterator(); | |
const bucket* it_; | |
const bucket* const buckets_; | |
const size_t size_; | |
friend class nanahan::Map<Key,Value,Hash,Pred,Alloc>; | |
}; | |
Map(size_t initial_size = 8) | |
:bucket_size_(initial_size), | |
buckets_(new bucket[bucket_size_]), | |
used_size_(0), | |
extending_(false) | |
{} | |
Map(const Map& orig) | |
:bucket_size_(orig.bucket_size_), | |
buckets_(new bucket[bucket_size_]), | |
used_size_(0), | |
extending_(false) | |
{ | |
iterator it = orig.begin(); | |
for(; it != orig.end(); ++it){ | |
insert(*it); | |
} | |
} | |
Map& operator=(const Map& orig){ | |
for(size_t i = 0; i < bucket_size_; ++i){ | |
if(buckets_[i].kvp_ != NULL){ | |
buckets_[i].kvp_->~Kvp(); | |
allocator_.deallocate(buckets_[i].kvp_, 1); | |
} | |
buckets_[i].slot_ = 0; | |
} | |
} | |
/* insert with std::pair */ | |
inline std::pair<iterator, bool> insert(const std::pair<const Key, Value>& kvp) | |
{ | |
const size_t hashvalue = Hash()(kvp.first); | |
const size_t target_slot = locate(hashvalue, 0); | |
//std::cout << "target slot:" << target_slot << std::endl; | |
bucket* target_bucket = buckets_ + target_slot; | |
iterator searched = find(kvp.first, hashvalue); | |
if(!searched.is_end()){ | |
return std::make_pair(searched, false); | |
} | |
//std::cout << "insert: " << kvp.first << std::endl; | |
/* search empty bucket */ | |
bucket* empty_bucket = target_bucket; | |
size_t distance = 0; | |
size_t index = target_slot; | |
const size_t max_distance = (HOP_RANGE < bucket_size_ ? | |
HOP_RANGE : bucket_size_); | |
do{ | |
if(empty_bucket->kvp_ == NULL) break; | |
index = (index + 1) & (bucket_size_ - 1); | |
empty_bucket = &buckets_[index]; | |
++distance; | |
}while(distance < max_distance); | |
if(max_distance <= distance){ | |
bucket_extend(); | |
return insert(kvp); | |
} | |
/* move empty bucket if it was too far */ | |
while(empty_bucket != NULL && SLOTSIZE <= distance){ | |
/* | |
std::cout << "find closer bucket:" << std::endl; | |
std::cout << "distance : " << distance << " => "; | |
//*/ | |
find_closer_bucket(&empty_bucket, &distance); | |
//std::cout << distance << std::endl; | |
} | |
if(empty_bucket == NULL){ | |
bucket_extend(); | |
return insert(kvp); | |
} | |
assert(empty_bucket->kvp_ == NULL); | |
empty_bucket->kvp_ = allocator_.allocate(1); | |
allocator_.construct(empty_bucket->kvp_, kvp); | |
//std::cout << "dist:" << distance << std::endl; | |
target_bucket->slot_ |= one << distance; | |
++used_size_; | |
//dump(); | |
return std::make_pair(iterator(empty_bucket, buckets_, bucket_size_), | |
true); | |
} | |
void bucket_extend(){ | |
const size_t old_size = bucket_size_; | |
size_t new_size = old_size * 2; | |
/* | |
std::cout << "resize from " << used_size_ << " " | |
<< old_size << " -> " << new_size << std::endl; | |
std::cout << "extending dencity: " << | |
used_size_ << " / " << bucket_size_ << "\t| " << | |
static_cast<double>(used_size_) * 100 / bucket_size_ << " %" << std::endl; | |
//*/ | |
while(true){ | |
Map new_map(new_size); | |
bool ok = true; | |
iterator end_it = end(); | |
for(iterator it = begin(); it != end_it; ++it){ | |
const bool result = new_map.insert_unsafe(&*it); | |
if(!result){ // failed to insert | |
std::cout << "failed to insert_unsafe()" << std::endl; | |
ok = false; | |
break; | |
} | |
} | |
if(!ok){ | |
new_size *= 2; | |
new_map.clear_unsafe(); | |
continue; | |
}else{ | |
/* | |
std::cout << "old map" << std::endl; | |
dump(); | |
std::cout << "to move map" << std::endl; | |
new_map.dump(); | |
//*/ | |
std::swap(buckets_, new_map.buckets_); | |
bucket_size_ = new_size; | |
std::swap(allocator_, new_map.allocator_); | |
new_map.clear_unsafe(); | |
/* | |
std::cout << "new map" << std::endl; | |
dump(); | |
std::cout << "deleting map" << std::endl; | |
new_map.dump(); | |
*/ | |
break; | |
} | |
} | |
} | |
/* erase the data*/ | |
iterator erase(iterator where) | |
{ | |
bucket* const target = where.it_; | |
bucket* start_bucket = target; | |
if(where.is_end()){ | |
//std::cout << "erase: end() passed"<< std::endl; | |
return end(); | |
} | |
if(find(where->first).is_end()){return end();} | |
//std::cout << "erase!" << slot->kvp_->first << std::endl; | |
allocator_.destroy(target->kvp_); | |
allocator_.deallocate(target->kvp_, 1); | |
target->kvp_ = NULL; | |
for(Slot i = 1; i != 0; i <<= 1){ | |
if((start_bucket->slot_ & i) != 0){ | |
--used_size_; | |
start_bucket->slot_ &= ~i; | |
return iterator(target , buckets_, bucket_size_); | |
} | |
--start_bucket; | |
} | |
//std::cout << "erased:" << std::endl; | |
return end(); | |
} | |
iterator find(const Key& key) | |
{ | |
return find(key, Hash()(key)); | |
} | |
iterator find(const Key& key, const size_t hashvalue) | |
{ | |
const unsigned int target = locate(hashvalue, 0); | |
bucket *target_bucket = buckets_ + target; | |
Slot slot_info = target_bucket->slot_; | |
/* | |
std::cout << "search:[" << target << "] for " << key <<std::endl; | |
//*/ | |
Pred pred; // comperator | |
while(slot_info){ | |
/* | |
std::cout << "trying " << slot_info << " "; | |
target_bucket->dump(); | |
std::cout << std::endl; | |
//*/ | |
if((slot_info & 1)){ | |
assert(target_bucket); | |
if(pred(target_bucket->kvp_->first, key)){ | |
return iterator(target_bucket, buckets_, bucket_size_); | |
} | |
slot_info &= ~1; | |
} | |
const size_t gap = detail::bitcount((~slot_info) & (slot_info - 1)); | |
slot_info >>= gap; | |
//std::cout << "new slot_info:" << slot_info << " gap:" << gap << std::endl; | |
target_bucket= forward(target_bucket, gap); | |
} | |
//std::cout << "not found" << std::endl; | |
return end(); | |
} | |
inline void find_closer_bucket(bucket** free_bucket, size_t* distance) | |
{ | |
bucket* move_bucket = *free_bucket - SLOTSIZE + 1; | |
if(move_bucket < buckets_){ | |
move_bucket += bucket_size_; | |
} | |
for(size_t i = SLOTSIZE - 1; 0 < i; --i, move_bucket = next(move_bucket)){ | |
for(size_t j = 0; j <= i; ++j){ | |
if(move_bucket->slot_ & (one << j)){ | |
(*free_bucket)->kvp_ = bucket_index(move_bucket, j).kvp_; | |
/* | |
std::cout << "moving from " << j << " to " << i << "bucket"; | |
bucket_index(move_bucket, j).dump(); | |
std::cout << std::endl; | |
//*/ | |
bucket_index(move_bucket,j).kvp_ = NULL; | |
move_bucket->slot_ &= ~(one << j); | |
move_bucket->slot_ |= one << i; | |
*free_bucket = &bucket_index(move_bucket,j); | |
*distance -= i - j; | |
return; | |
} | |
} | |
} | |
*free_bucket = NULL; | |
*distance = 0; | |
} | |
const_iterator begin()const{ | |
bucket* head = buckets_; | |
while(head->kvp_ == NULL && head != &buckets_[bucket_size_]){++head;} | |
return iterator(head, buckets_, bucket_size_); | |
} | |
const_iterator end()const{ | |
return iterator(&buckets_[bucket_size_], buckets_, bucket_size_); | |
} | |
iterator begin(){ | |
bucket* head = buckets_; | |
while(head->kvp_ == NULL && head != &buckets_[bucket_size_]){++head;} | |
return iterator(head, buckets_, bucket_size_); | |
} | |
iterator end(){ | |
return iterator(&buckets_[bucket_size_], buckets_, bucket_size_); | |
} | |
size_t size()const{return used_size_;} | |
bool empty()const{return used_size_ == 0;} | |
void dump()const{ | |
for(size_t i=0; i < bucket_size_; ++i){ | |
std::cout << "[" << i << "] "; | |
buckets_[i].dump(); | |
std::cout << std::endl; | |
} | |
std::cout << "total:" << used_size_ << std::endl; | |
} | |
void clear(){ | |
for(size_t i = 0; i < bucket_size_; ++i){ | |
if(buckets_[i].kvp_ != NULL){ | |
buckets_[i].kvp_->~Kvp(); | |
allocator_.deallocate(buckets_[i].kvp_, 1); | |
} | |
buckets_[i].slot_ = 0; | |
} | |
delete[] buckets_; | |
buckets_ = new bucket[INITIAL_SIZE]; | |
bucket_size_ = INITIAL_SIZE; | |
used_size_ = 0; | |
} | |
~Map(){ | |
//dump(); | |
if(!extending_){ | |
clear(); | |
} | |
delete[] buckets_; | |
} | |
private: | |
inline void clear_unsafe(){ | |
extending_ = true; | |
return; | |
} | |
inline bool insert_unsafe(std::pair<const Key, Value>* const kvp) | |
{ | |
const size_t hashvalue = Hash()(kvp->first); | |
const size_t target_slot = hashvalue & (bucket_size_ - 1); | |
bucket* target_bucket = buckets_+ target_slot; | |
//std::cout << "insert: " << kvp.first << std::endl; | |
/* search empty bucket */ | |
bucket* empty_bucket = target_bucket; | |
size_t distance = 0; | |
size_t index = target_slot; | |
const size_t max_distance = (HOP_RANGE < bucket_size_ ? | |
HOP_RANGE : bucket_size_); | |
do{ | |
if(empty_bucket->kvp_ == NULL) break; | |
index = (index + 1) & (bucket_size_ - 1); | |
empty_bucket = buckets_ + index; | |
++distance; | |
}while(distance < max_distance); | |
if(max_distance <= distance){ | |
return false; | |
} | |
/* move empty bucket if it was too far */ | |
while(empty_bucket != NULL && SLOTSIZE <= distance){ | |
find_closer_bucket(&empty_bucket, &distance); | |
} | |
if(empty_bucket == NULL){ | |
return false; | |
} | |
assert(empty_bucket->kvp_ == NULL); | |
empty_bucket->kvp_ = kvp; | |
target_bucket->slot_ |= one << distance; | |
return true; | |
} | |
inline size_t locate(size_t start, size_t diff)const{ | |
return (start + diff) & (bucket_size_ - 1); | |
} | |
inline bucket* next(bucket* b){ | |
return forward(b, 1); | |
} | |
inline bucket* forward(bucket* b, size_t stride)const{ | |
return (b + stride) < &buckets_[bucket_size_] ? | |
b + stride : b + stride - bucket_size_; | |
} | |
inline bucket& bucket_index(bucket* start, size_t index){ | |
bucket* target = start + index; | |
return target < &buckets_[bucket_size_] ? *target : *(target - bucket_size_); | |
} | |
uint64_t bucket_size_; | |
bucket* buckets_; | |
size_t used_size_; | |
Alloc allocator_; | |
bool extending_; | |
}; | |
} // namespace nanahan | |
#undef nanahan_64bit | |
#undef one | |
#undef slot_size | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment