00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #ifndef __HASHTABLE_HPP_
00022 #define __HASHTABLE_HPP_
00023
00024 #include <memory>
00025 #include <stdexcept>
00026 #include <deque>
00027 #include <algorithm>
00028 #include <boost/thread/shared_mutex.hpp>
00029 #include <boost/thread/mutex.hpp>
00030
00031 using namespace boost;
00032
00033
00037 template<class KeyT,
00038 class StoreT,
00039 class Allocator = std::allocator< std::pair<KeyT, StoreT> > >
00040 class HashMap;
00041
00042 const size_t table_sizes[26] = { 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613,
00043 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653,
00044 100663319, 201326611, 402653189, 805306457, 1610612741 };
00045
00046 template<class KeyT, class StoreT, class Allocator>
00047 class HashMap
00048 {
00049 static const size_t globalBaseSize = 53;
00050 static const size_t upperFillLimit = 50;
00051
00052 typedef std::pair<KeyT, StoreT> storeType;
00053
00054 private:
00055 size_t(*hash)(size_t maxSize, const KeyT& val);
00056 size_t(*hash2)(size_t maxSize, const KeyT& val);
00057 int *refC;
00058 std::deque<HashMap<KeyT, StoreT, Allocator>*>* ref;
00059 Allocator alloc;
00060 storeType *content;
00061 size_t contentSize;
00062 size_t allocated;
00063 unsigned char *allocateStatus;
00064 int sizeIndex;
00065
00066 enum HashMapAllocateStatus{
00067 HashMapAllocateStatus_free = 0,
00068 HashMapAllocateStatus_deleted = 1,
00069 HashMapAllocateStatus_used = 2
00070 };
00071
00072
00073 public:
00074 shared_mutex hashTableLock;
00075 shared_mutex *hashElementLock;
00076 mutex checkMutex;
00077 mutex *hashElementMutex;
00078
00079 private:
00080
00081 void build(size_t baseSize = globalBaseSize) {
00082
00083 hashTableLock.lock();
00084
00085 refC = new int;
00086 *refC = 1;
00087 ref = new std::deque<HashMap<KeyT, StoreT, Allocator>*>;
00088 ref->push_back(this);
00089 contentSize = baseSize;
00090 content = alloc.allocate(contentSize);
00091 allocateStatus = new unsigned char[contentSize];
00092 memset(allocateStatus, HashMapAllocateStatus_free, contentSize);
00093 allocated = 0;
00094 hashElementMutex = new mutex[contentSize];
00095 hashElementLock = new shared_mutex[contentSize];
00096
00097 hashTableLock.unlock();
00098 }
00099 void build(const HashMap& reference) {
00100
00101 hashTableLock.lock();
00102 refC = reference.refC;
00103 ref = reference.ref;
00104 (*refC)++;
00105 ref->push_back(this);
00106 contentSize = reference.contentSize;
00107 content = reference.content;
00108 allocateStatus = reference.allocateStatus;
00109 allocated = reference.allocated;
00110
00111 hashTableLock.unlock();
00112 }
00113
00114 void reallocate() {
00115
00116 hashTableLock.lock();
00117
00118 storeType* oldContent = content;
00119 unsigned char* oldAllocateStatus = allocateStatus;
00120
00121 size_t oldContentSize = contentSize;
00122
00123 contentSize = table_sizes[++sizeIndex];
00124 content = alloc.allocate(contentSize);
00125 allocateStatus = new unsigned char[contentSize];
00126 memset(allocateStatus, HashMapAllocateStatus_free, contentSize);
00127 allocated = 0;
00128 delete[] hashElementMutex;
00129 delete[] hashElementLock;
00130 hashElementMutex = new mutex[contentSize];
00131 hashElementLock = new shared_mutex[contentSize];
00132
00133 debug("REALLOCATE %lld -> %lld\n", (long long)oldContentSize, (long long)contentSize);
00134
00135 for( size_t p=0;p<oldContentSize;p++ ) {
00136 if(oldAllocateStatus[p] == HashMapAllocateStatus_used) {
00137
00138 size_t hashVal = hash( contentSize, oldContent[p].first );
00139 size_t hashVal2 = hash2( contentSize, oldContent[p].first );
00140
00141 size_t failPos = hashVal;
00142 size_t hashFix = 1;
00143 while(true) {
00144 if( allocateStatus[failPos] != HashMapAllocateStatus_used ) {
00145
00146 break;
00147 } else {
00148
00149 failPos = (hashVal + hashFix*hashVal2) %contentSize;
00150 hashFix++;
00151 }
00152 }
00153
00154 allocateStatus[failPos] = HashMapAllocateStatus_used;
00155 alloc.construct( content+failPos, oldContent[p] );
00156 alloc.destroy( oldContent+p );
00157 allocated++;
00158 }
00159 }
00160 alloc.deallocate( oldContent, oldContentSize );
00161 delete[] oldAllocateStatus;
00162 hashTableLock.unlock();
00163 }
00164 public:
00168 HashMap( size_t(*hashFunction)(size_t maxSize, const KeyT& val) ): hash(hashFunction), hash2(hashFunction), sizeIndex(0) {
00169 build();
00170 }
00174 HashMap( size_t(*hashFunction)(size_t maxSize, const KeyT& val), size_t(*hashFunction2)(size_t maxSize, const KeyT& val)): hash(hashFunction), hash2(hashFunction2), sizeIndex(0) {
00175 build();
00176 }
00180 HashMap( const HashMap& reference ): hash(reference.hash) {
00181 build(reference);
00182 }
00186 size_t size(){
00187 return allocated;
00188 }
00192 void forward_allocate(size_t to_be_filled) {
00193
00194
00195 while(table_sizes[sizeIndex] < to_be_filled) {
00196 sizeIndex++;
00197 }
00198
00199 reallocate();
00200 }
00204 HashMap& operator=( const HashMap& reference ) {
00205 if(&reference == this) {
00206 return *this;
00207 }
00208
00209 this->~HashMap();
00210 build(reference);
00211 return *this;
00212 }
00213 ~HashMap() {
00214 delete refC;
00215 delete ref;
00216 for( size_t p=0;p<contentSize;p++ ) {
00217 if(allocateStatus[p] == HashMapAllocateStatus_used) {
00218 alloc.destroy( content+p );
00219 }
00220 }
00221 alloc.deallocate(content, contentSize);
00222 delete[] allocateStatus;
00223 delete[] hashElementMutex;
00224 delete[] hashElementLock;
00225
00226 }
00227
00231 void lock_table() {
00232 hashTableLock.lock();
00233 }
00237 void unlock_table() {
00238 hashTableLock.unlock();
00239 }
00240
00244 storeType& first(long long &position) {
00245 for(long long i=0; i<(long long)contentSize; i++)
00246 if( allocateStatus[i] == HashMapAllocateStatus_used) {
00247 position = i;
00248 break;
00249 } else if (i == (long long)contentSize - 1) {
00250 position = (long long)contentSize;
00251 }
00252
00253 if(position >= (long long)contentSize ) {
00254 position = -1;
00255 return content[0];
00256 }
00257
00258 return content[position];
00259 }
00263 storeType& next(long long &position) {
00264 position++;
00265 for(long long i=position; i<(long long)contentSize; i++)
00266 if( allocateStatus[i] == HashMapAllocateStatus_used) {
00267 position = i;
00268 break;
00269 } else if (i == (long long)contentSize - 1) {
00270 position = (long long)contentSize;
00271 }
00272
00273 if(position >= (long long)contentSize ) {
00274 position = -1;
00275 return content[0];
00276 }
00277
00278 return content[position];
00279 }
00280
00281
00285 StoreT& operator[](const KeyT &key) {
00286
00287 hashTableLock.lock_shared();
00288
00289 size_t hashVal = hash( contentSize, key );
00290 size_t hashVal2 = hash2( contentSize, key );
00291
00292 size_t failPos = hashVal;
00293 size_t hashFix = 1;
00294 while(true) {
00295 hashElementLock[failPos].lock_shared();
00296 if( allocateStatus[failPos] != HashMapAllocateStatus_used ) {
00297
00298 hashElementMutex[failPos].lock();
00299
00300 hashElementLock[failPos].unlock_shared();
00301 hashElementLock[failPos].lock();
00302
00303 hashElementMutex[failPos].unlock();
00304 break;
00305 } else {
00306 if( content[failPos].first == key ) {
00307
00308 hashElementLock[failPos].unlock_shared();
00309 hashTableLock.unlock_shared();
00310 return content[failPos].second;
00311 }
00312 hashElementLock[failPos].unlock_shared();
00313
00314 failPos = (hashVal + hashFix*hashVal2) % contentSize;
00315 hashFix++;
00316 }
00317 }
00318
00319
00320 storeType h = std::make_pair( key, StoreT() );
00321
00322 allocateStatus[failPos] = HashMapAllocateStatus_used;
00323 alloc.construct( content+failPos, h );
00324 allocated++;
00325
00326 hashElementLock[failPos].unlock();
00327 hashTableLock.unlock_shared();
00328
00329 return (*this)[key];
00330 }
00334 void insert( std::pair<KeyT, StoreT> val ) {
00335 checkMutex.lock();
00336 hashTableLock.lock_shared();
00337
00338 if(allocated * 100 / contentSize >= upperFillLimit) {
00339 hashTableLock.unlock_shared();
00340 reallocate();
00341 hashTableLock.lock_shared();
00342 }
00343
00344 checkMutex.unlock();
00345
00346
00347
00348
00349 size_t hashVal = hash( contentSize, val.first );
00350 size_t hashVal2 = hash2( contentSize, val.first );
00351
00352 size_t failPos = hashVal;
00353 size_t hashFix = 1;
00354 while(true) {
00355 hashElementLock[failPos].lock_shared();
00356 if( allocateStatus[failPos] != HashMapAllocateStatus_used ) {
00357
00358 hashElementMutex[failPos].lock();
00359
00360 hashElementLock[failPos].unlock_shared();
00361 hashElementLock[failPos].lock();
00362
00363 hashElementMutex[failPos].unlock();
00364
00365 break;
00366 } else {
00367
00368 if( content[failPos].first == val.first ) {
00369 hashElementMutex[failPos].lock();
00370
00371 hashElementLock[failPos].unlock_shared();
00372 hashElementLock[failPos].lock();
00373
00374 hashElementMutex[failPos].unlock();
00375
00376 content[failPos].second = val.second;
00377
00378 hashElementLock[failPos].unlock();
00379 hashTableLock.unlock_shared();
00380 return;
00381 }
00382
00383 hashElementLock[failPos].unlock_shared();
00384
00385
00386
00387 failPos = (hashVal + hashFix*hashVal2) % contentSize;
00388 hashFix++;
00389 }
00390 }
00391
00392 allocateStatus[failPos] = HashMapAllocateStatus_used;
00393 alloc.construct( content+failPos, val );
00394 allocated++;
00395
00396 hashElementLock[failPos].unlock();
00397
00398
00399
00400
00401
00402
00403 hashTableLock.unlock_shared();
00404
00405 }
00406 void insert( KeyT key, StoreT val ) {
00407 insert(std::pair<KeyT, StoreT>(key, val));
00408 }
00409
00413 void erase(const KeyT& key ) {
00414
00415
00416 hashTableLock.lock_shared();
00417
00418 size_t hashVal = hash( contentSize, key );
00419 size_t hashVal2 = hash2( contentSize, key );
00420
00421 size_t failPos = hashVal;
00422 size_t hashFix = 1;
00423 while(true) {
00424 hashElementLock[failPos].lock_shared();
00425 if( allocateStatus[failPos] == HashMapAllocateStatus_used && content[failPos].first == key) {
00426 hashElementMutex[failPos].lock();
00427
00428 hashElementLock[failPos].unlock_shared();
00429 hashElementLock[failPos].lock();
00430
00431 hashElementMutex[failPos].unlock();
00432
00433 break;
00434 } else if ( allocateStatus[failPos] == HashMapAllocateStatus_free ) {
00435
00436 hashElementLock[failPos].unlock_shared();
00437 hashTableLock.unlock_shared();
00438 return;
00439 } else {
00440 hashElementLock[failPos].unlock_shared();
00441
00442 failPos = (hashVal + hashFix*hashVal2) % contentSize;
00443 hashFix++;
00444 }
00445 }
00446
00447
00448 allocateStatus[failPos] = HashMapAllocateStatus_deleted;
00449 alloc.destroy( content+failPos );
00450 allocated--;
00451 hashElementLock[failPos].unlock();
00452
00453
00454
00455
00456 hashTableLock.unlock_shared();
00457 }
00458
00462 void unlock(const KeyT &key){
00463
00464 size_t hashVal = hash( contentSize, key );
00465 size_t hashVal2 = hash2( contentSize, key );
00466
00467 size_t failPos = hashVal;
00468 size_t hashFix = 1;
00469 while(true) {
00470 hashElementLock[failPos].lock_shared();
00471 if( allocateStatus[failPos] == HashMapAllocateStatus_used && content[failPos].first == key ) {
00472
00473 hashElementLock[failPos].unlock_shared();
00474 hashElementLock[failPos].unlock_shared();
00475 hashTableLock.unlock_shared();
00476 return;
00477 }
00478 hashElementLock[failPos].unlock_shared();
00479
00480 failPos = (hashVal + hashFix*hashVal2) % contentSize;
00481 hashFix++;
00482 }
00483
00484 }
00485
00489 StoreT* find_lock(const KeyT &key) {
00490
00491
00492
00493 hashTableLock.lock_shared();
00494 size_t hashVal = hash( contentSize, key );
00495 size_t hashVal2 = hash2( contentSize, key );
00496
00497 size_t failPos = hashVal;
00498 size_t hashFix = 1;
00499 while(true) {
00500 hashElementLock[failPos].lock_shared();
00501 if( allocateStatus[failPos] == HashMapAllocateStatus_free ) {
00502 hashElementLock[failPos].unlock_shared();
00503 hashTableLock.unlock_shared();
00504 return 0;
00505 } else {
00506 if( allocateStatus[failPos] == HashMapAllocateStatus_used && content[failPos].first == key ) {
00507
00508
00509 return &content[failPos].second;
00510 }
00511 hashElementLock[failPos].unlock_shared();
00512
00513 failPos = (hashVal + hashFix*hashVal2) % contentSize;
00514
00515 hashFix++;
00516 }
00517 }
00518
00519 }
00520
00524 StoreT* find(const KeyT &key) {
00525
00526
00527
00528 hashTableLock.lock_shared();
00529 size_t hashVal = hash( contentSize, key );
00530 size_t hashVal2 = hash2( contentSize, key );
00531
00532 size_t failPos = hashVal;
00533 size_t hashFix = 1;
00534 while(true) {
00535 hashElementLock[failPos].lock_shared();
00536 if( allocateStatus[failPos] == HashMapAllocateStatus_free ) {
00537 hashElementLock[failPos].unlock_shared();
00538 hashTableLock.unlock_shared();
00539 return 0;
00540 } else {
00541 if( allocateStatus[failPos] == HashMapAllocateStatus_used && content[failPos].first == key ) {
00542
00543 hashElementLock[failPos].unlock_shared();
00544 hashTableLock.unlock_shared();
00545 return &content[failPos].second;
00546 }
00547 hashElementLock[failPos].unlock_shared();
00548
00549 failPos = (hashVal + hashFix*hashVal2) % contentSize;
00550
00551 hashFix++;
00552 }
00553 }
00554
00555 }
00559 void clear() {
00560
00561
00562 for( size_t p=0;p<contentSize;p++ ) {
00563 if(allocateStatus[p] == HashMapAllocateStatus_used) {
00564 alloc.destroy( content+p );
00565 }
00566 }
00567 memset(allocateStatus, HashMapAllocateStatus_free, contentSize);
00568 allocated = 0;
00569 }
00570 };
00571
00572 #endif