| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * License); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| #ifndef COMMON_CONTAINER_HASH_TABLE_H |
| #define COMMON_CONTAINER_HASH_TABLE_H |
| |
| #include "common/allocator/alloc_base.h" |
| #include "common/container/array.h" |
| #include "common/container/hash_func.h" |
| #include "common/container/hash_node.h" |
| #include "common/container/hash_segm.h" |
| |
| #define TABLE_CAPACITY 100 |
| #define TABLE_GROWTH 2 |
| |
| namespace common { |
| |
| template <typename KeyType, typename ValueType, typename F> |
| class HashTable { |
| public: |
| HashTable() : table_(TABLE_CAPACITY) { |
| new_table_capacity_ = TABLE_CAPACITY; |
| old_table_capacity_ = TABLE_CAPACITY; |
| size_ = 0; |
| is_inited_ = false; |
| hash_version_ = 0; |
| is_extending_ = 0; |
| } |
| |
| HashTable(size_t total_capacity) |
| : table_(total_capacity / SEGMENT_CAPACITY) { |
| new_table_capacity_ = total_capacity / SEGMENT_CAPACITY; |
| old_table_capacity_ = new_table_capacity_; |
| size_ = 0; |
| is_inited_ = false; |
| hash_version_ = 0; |
| is_extending_ = 0; |
| } |
| ~HashTable() { |
| new_table_capacity_ = 0; |
| size_ = 0; |
| is_inited_ = false; |
| hash_version_ = 0; |
| is_extending_ = 0; |
| } |
| |
| int init() { |
| if (is_inited_) { |
| // log_err("init repeated."); |
| ASSERT(!is_inited_); |
| } |
| |
| int ret = E_OK; |
| ret = table_.init(); |
| if (ret != E_OK) { |
| return E_OOM; |
| } |
| for (size_t i = 0; i < new_table_capacity_; i++) { |
| void* buf = |
| mem_alloc(sizeof(HashSegm<KeyType, ValueType>), MOD_HASH_TABLE); |
| if (UNLIKELY(nullptr == buf)) { |
| // log_err("malloc() failed."); |
| return E_OOM; |
| } |
| HashSegm<KeyType, ValueType>* tmp_segm = |
| new (buf) HashSegm<KeyType, ValueType>; |
| table_.append(tmp_segm); |
| } |
| is_inited_ = true; |
| return E_OK; |
| } |
| |
| void destroy() { |
| if (is_inited_) { |
| for (size_t i = 0; i < new_table_capacity_; i++) { |
| for (size_t j = 0; j < SEGMENT_CAPACITY; j++) { |
| if ((*table_[i])[j].get_valid()) { |
| HashNode<KeyType, ValueType>* prev = nullptr; |
| HashNode<KeyType, ValueType>* entry = |
| (*table_[i])[j].get_next(); |
| while (entry != nullptr) { |
| prev = entry; |
| entry = entry->get_next(); |
| prev->~HashNode(); |
| mem_free(prev); |
| prev = nullptr; |
| } |
| (*table_[i])[j].set_valid(false); |
| } |
| } |
| table_[i]->~HashSegm(); |
| mem_free(table_[i]); |
| table_[i] = nullptr; |
| } |
| table_.destroy(); |
| } |
| is_inited_ = false; |
| } |
| |
| int table_extend() { |
| int ret = table_.extend(); |
| if (ret != E_OK) { |
| return ret; |
| } |
| for (size_t i = ATOMIC_LOAD(&new_table_capacity_); |
| i < table_.capacity(); i++) { |
| void* buf = |
| mem_alloc(sizeof(HashSegm<KeyType, ValueType>), MOD_HASH_TABLE); |
| if (UNLIKELY(nullptr == buf)) { |
| // log_err("malloc() failed."); |
| return E_OOM; |
| } |
| HashSegm<KeyType, ValueType>* tmp_segm = |
| new (buf) HashSegm<KeyType, ValueType>; |
| table_.append(tmp_segm); |
| } |
| return E_OK; |
| } |
| |
| int move_bucket_head(size_t from_segment_idx, size_t from_bucket_idx) { |
| HashNode<KeyType, ValueType>* prev = |
| &((*table_[from_segment_idx])[from_bucket_idx]); |
| HashNode<KeyType, ValueType>* entry = |
| ((*table_[from_segment_idx])[from_bucket_idx]).get_next(); |
| |
| while ( |
| (*table_[from_segment_idx])[from_bucket_idx].get_rehashid() != |
| ATOMIC_LOAD(&hash_version_)) { // if the hashnode hasn't rehashed |
| uint32_t hash_value = hashfunc_( |
| (*table_[from_segment_idx])[from_bucket_idx].get_key()); |
| size_t to_segment_idx = |
| hash_value % ATOMIC_LOAD(&new_table_capacity_); |
| size_t to_bucket_idx = hash_value % SEGMENT_CAPACITY; |
| if (from_segment_idx == to_segment_idx && |
| from_bucket_idx == to_bucket_idx) { |
| ((*table_[to_segment_idx])[to_bucket_idx]) |
| .set_rehashid( |
| ATOMIC_LOAD(&hash_version_)); // only update rehashid |
| } else { |
| // put |
| table_[to_segment_idx]->mutexes_[to_bucket_idx].lock(); |
| int ret = put_nolock( |
| (*table_[from_segment_idx])[from_bucket_idx].get_key(), |
| (*table_[from_segment_idx])[from_bucket_idx].get_value(), |
| to_segment_idx, to_bucket_idx); |
| table_[to_segment_idx]->mutexes_[to_bucket_idx].unlock(); |
| if (ret != E_OK) { |
| return ret; |
| } |
| // remove |
| if (entry != nullptr) { |
| ((*table_[from_segment_idx])[from_bucket_idx]) |
| .set_key(entry->get_key()); |
| ((*table_[from_segment_idx])[from_bucket_idx]) |
| .set_value(entry->get_value()); |
| ((*table_[from_segment_idx])[from_bucket_idx]) |
| .set_next(entry->get_next()); |
| ((*table_[from_segment_idx])[from_bucket_idx]) |
| .set_valid(true); |
| ((*table_[from_segment_idx])[from_bucket_idx]) |
| .set_rehashid(entry->get_rehashid()); |
| ATOMIC_AAF(&size_, -1); |
| entry->~HashNode(); |
| mem_free(entry); |
| entry = prev->get_next(); |
| } else { |
| ((*table_[from_segment_idx])[from_bucket_idx]) |
| .set_valid(false); |
| ATOMIC_AAF(&size_, -1); |
| break; |
| } |
| } |
| } |
| |
| return E_OK; |
| } |
| |
| int move_bucket_chain(size_t from_segment_idx, size_t from_bucket_idx) { |
| HashNode<KeyType, ValueType>* prev = |
| &((*table_[from_segment_idx])[from_bucket_idx]); |
| HashNode<KeyType, ValueType>* entry = |
| ((*table_[from_segment_idx])[from_bucket_idx]).get_next(); |
| |
| while (entry != nullptr) { |
| if (entry->get_rehashid() != ATOMIC_LOAD(&hash_version_)) { |
| uint32_t hash_value = hashfunc_(entry->get_key()); |
| size_t to_segment_idx = |
| hash_value % ATOMIC_LOAD(&new_table_capacity_); |
| size_t to_bucket_idx = hash_value % SEGMENT_CAPACITY; |
| if (from_segment_idx == to_segment_idx && |
| from_bucket_idx == to_bucket_idx) { |
| entry->set_rehashid(ATOMIC_LOAD(&hash_version_)); |
| prev = entry; |
| entry = entry->get_next(); |
| } else { |
| table_[to_segment_idx]->mutexes_[to_bucket_idx].lock(); |
| if ((*table_[to_segment_idx])[to_bucket_idx].get_valid() == |
| false) { |
| ((*table_[to_segment_idx])[to_bucket_idx]) |
| .set_key(entry->get_key()); |
| ((*table_[to_segment_idx])[to_bucket_idx]) |
| .set_value(entry->get_value()); |
| ((*table_[to_segment_idx])[to_bucket_idx]) |
| .set_next(nullptr); |
| ((*table_[to_segment_idx])[to_bucket_idx]) |
| .set_valid(true); |
| ((*table_[to_segment_idx])[to_bucket_idx]) |
| .set_rehashid(ATOMIC_LOAD(&hash_version_)); |
| prev->set_next(entry->get_next()); |
| entry->~HashNode(); |
| mem_free(entry); |
| entry = prev->get_next(); |
| // entry = entry->get_next(); |
| } else { |
| entry->set_rehashid(ATOMIC_LOAD(&hash_version_)); |
| prev->set_next(entry->get_next()); |
| entry->set_next((*table_[to_segment_idx])[to_bucket_idx] |
| .get_next()); |
| (*table_[to_segment_idx])[to_bucket_idx].set_next( |
| entry); |
| entry = prev->get_next(); |
| } |
| table_[to_segment_idx]->mutexes_[to_bucket_idx].unlock(); |
| } |
| } else { |
| prev = prev->get_next(); |
| entry = entry->get_next(); |
| } |
| } |
| return E_OK; |
| } |
| |
| int extend() { |
| int ret = table_extend(); |
| if (ret != E_OK) { |
| return ret; |
| } |
| ATOMIC_STORE(&old_table_capacity_, ATOMIC_LOAD(&new_table_capacity_)); |
| ATOMIC_STORE(&new_table_capacity_, table_.capacity()); |
| // ATOMIC_STORE(&hash_total_capacity_, table_.capacity() * |
| // SEGMENT_CAPACITY); |
| |
| int64_t next_hash_version = |
| ATOMIC_LOAD(&hash_version_) + |
| 1; // the highest position of next_hash_version is 0 |
| ATOMIC_STORE(&hash_version_, |
| next_hash_version | |
| (1UL << 63)); // set the highest position to 1 |
| // which means 'extend' is beginning. |
| |
| // rehash |
| for (size_t from_segment_idx = 0; |
| from_segment_idx < ATOMIC_LOAD(&old_table_capacity_); |
| from_segment_idx++) { |
| for (size_t from_bucket_idx = 0; from_bucket_idx < SEGMENT_CAPACITY; |
| from_bucket_idx++) { |
| table_[from_segment_idx]->mutexes_[from_bucket_idx].lock(); |
| if ((*table_[from_segment_idx])[from_bucket_idx].get_valid()) { |
| ret = move_bucket_head(from_segment_idx, from_bucket_idx); |
| if (ret != E_OK) { |
| table_[from_segment_idx] |
| ->mutexes_[from_bucket_idx] |
| .unlock(); |
| return ret; |
| } |
| ret = move_bucket_chain(from_segment_idx, from_bucket_idx); |
| if (ret != E_OK) { |
| table_[from_segment_idx] |
| ->mutexes_[from_bucket_idx] |
| .unlock(); |
| return ret; |
| } |
| } |
| table_[from_segment_idx]->mutexes_[from_bucket_idx].unlock(); |
| } |
| } |
| |
| ATOMIC_STORE( |
| &hash_version_, |
| next_hash_version & (~(1UL << 63))); // set extending flag is false |
| |
| return E_OK; |
| } |
| |
| /* |
| * when the key already exists, just update old value to new value; |
| */ |
| int put(const KeyType& key, const ValueType& value) { |
| int ret; |
| uint32_t hash_value = hashfunc_(key); |
| |
| while (true) { |
| uint8_t cur_table_capacity = ATOMIC_LOAD(&new_table_capacity_); |
| size_t segment_idx = hash_value % cur_table_capacity; |
| size_t bucket_idx = hash_value % SEGMENT_CAPACITY; |
| |
| table_[segment_idx]->mutexes_[bucket_idx].lock(); |
| ret = put_nolock(key, value, segment_idx, bucket_idx); |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| if (ret != E_OK) { |
| return ret; |
| } |
| |
| if (UNLIKELY( |
| cur_table_capacity != |
| ATOMIC_LOAD( |
| &new_table_capacity_))) { // resolve the problem of |
| // insert into old position |
| ret = remove_from_wrong_bucket(key, segment_idx, bucket_idx); |
| // if (ret != E_OK) { |
| // return ret; |
| // } |
| continue; |
| } else { |
| break; |
| } |
| } |
| |
| if (ATOMIC_LOAD(&size_) > ATOMIC_LOAD(&new_table_capacity_) * |
| SEGMENT_CAPACITY * TABLE_GROWTH) { |
| uint8_t tmp = 0; |
| if (ATOMIC_CAS(&is_extending_, &tmp, 1)) { |
| ret = extend(); |
| if (ret != E_OK) { |
| ATOMIC_AAF(&is_extending_, -1); |
| return ret; |
| } |
| ATOMIC_AAF(&is_extending_, -1); |
| } |
| } |
| |
| return ret; |
| } |
| |
| /* |
| * when the key already exists, just update old value to new value; |
| */ |
| int put_nolock(const KeyType& key, const ValueType& value, |
| size_t segment_idx, size_t bucket_idx) { |
| if (((*table_[segment_idx])[bucket_idx]).get_valid() == |
| false) { // if current segment has no valid node |
| ((*table_[segment_idx])[bucket_idx]).set_key(key); |
| ((*table_[segment_idx])[bucket_idx]).set_value(value); |
| ((*table_[segment_idx])[bucket_idx]).set_next(nullptr); |
| ((*table_[segment_idx])[bucket_idx]).set_valid(true); |
| ((*table_[segment_idx])[bucket_idx]) |
| .set_rehashid(ATOMIC_LOAD(&hash_version_)); |
| ATOMIC_AAF(&size_, 1); |
| } else if (((*table_[segment_idx])[bucket_idx]).get_key() == |
| key) { // if current segment's first node's key is same as |
| // the target key |
| ((*table_[segment_idx])[bucket_idx]) |
| .set_value(value); // update value of existing node |
| ((*table_[segment_idx])[bucket_idx]) |
| .set_rehashid(ATOMIC_LOAD(&hash_version_)); |
| } else { // follow the link list |
| HashNode<KeyType, ValueType>* prev = |
| &((*table_[segment_idx])[bucket_idx]); |
| HashNode<KeyType, ValueType>* entry = |
| ((*table_[segment_idx])[bucket_idx]).get_next(); |
| while (entry != nullptr && entry->get_key() != key) { |
| prev = entry; |
| entry = entry->get_next(); |
| } |
| if (LIKELY(entry == nullptr)) { |
| void* buf = mem_alloc(sizeof(HashNode<KeyType, ValueType>), |
| MOD_HASH_TABLE); |
| if (UNLIKELY(nullptr == buf)) { |
| // log_err("malloc() failed."); |
| return E_OOM; |
| } |
| entry = new (buf) HashNode<KeyType, ValueType>; |
| entry->set_key(key); |
| entry->set_value(value); |
| entry->set_valid(true); |
| entry->set_rehashid(ATOMIC_LOAD(&hash_version_)); |
| prev->set_next(entry); |
| ATOMIC_AAF(&size_, 1); |
| } else { |
| entry->set_value(value); // update value of existing node |
| entry->set_rehashid(ATOMIC_LOAD(&hash_version_)); |
| } |
| } |
| return E_OK; |
| } |
| |
| /* |
| * when the key already exists, then remain the old value and return the old |
| * value and set 'exist' to true |
| */ |
| int put_no_update(const KeyType& key, ValueType& value, bool& exist) { |
| int ret; |
| uint32_t hash_value = hashfunc_(key); |
| |
| while (true) { |
| uint8_t cur_table_capacity = ATOMIC_LOAD(&new_table_capacity_); |
| size_t segment_idx = hash_value % cur_table_capacity; |
| size_t bucket_idx = hash_value % SEGMENT_CAPACITY; |
| |
| table_[segment_idx]->mutexes_[bucket_idx].lock(); |
| ret = put_nolock_no_update(key, value, segment_idx, bucket_idx, |
| exist); |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| if (ret != E_OK) { |
| return ret; |
| } |
| |
| if (UNLIKELY( |
| cur_table_capacity != |
| ATOMIC_LOAD( |
| &new_table_capacity_))) { // resolve the problem of |
| // insert into old position |
| ret = remove_from_wrong_bucket(key, segment_idx, bucket_idx); |
| // if (ret != E_OK) { |
| // return ret; |
| // } |
| continue; |
| } else { |
| break; |
| } |
| } |
| |
| if (ATOMIC_LOAD(&size_) > ATOMIC_LOAD(&new_table_capacity_) * |
| SEGMENT_CAPACITY * TABLE_GROWTH) { |
| uint8_t tmp = 0; |
| if (ATOMIC_CAS(&is_extending_, &tmp, 1)) { |
| ret = extend(); |
| if (ret != E_OK) { |
| return ret; |
| } |
| ATOMIC_AAF(&is_extending_, -1); |
| } |
| } |
| |
| return ret; |
| } |
| |
| /* |
| * when the key already exists, then remain the old value and return the old |
| * value and set 'exist' to true |
| */ |
| int put_nolock_no_update(const KeyType& key, ValueType& value, |
| size_t segment_idx, size_t bucket_idx, |
| bool& exist) { |
| if (((*table_[segment_idx])[bucket_idx]).get_valid() == |
| false) { // if current segment has no valid node |
| ((*table_[segment_idx])[bucket_idx]).set_key(key); |
| ((*table_[segment_idx])[bucket_idx]).set_value(value); |
| ((*table_[segment_idx])[bucket_idx]).set_next(nullptr); |
| ((*table_[segment_idx])[bucket_idx]).set_valid(true); |
| ((*table_[segment_idx])[bucket_idx]) |
| .set_rehashid(ATOMIC_LOAD(&hash_version_)); |
| ATOMIC_AAF(&size_, 1); |
| } else if (((*table_[segment_idx])[bucket_idx]).get_key() == |
| key) { // if current segment's first node's key is same as |
| // the target key |
| exist = true; |
| value = ((*table_[segment_idx])[bucket_idx]).get_value(); |
| ((*table_[segment_idx])[bucket_idx]) |
| .set_rehashid(ATOMIC_LOAD(&hash_version_)); |
| } else { // follow the link list |
| HashNode<KeyType, ValueType>* prev = |
| &((*table_[segment_idx])[bucket_idx]); |
| HashNode<KeyType, ValueType>* entry = |
| ((*table_[segment_idx])[bucket_idx]).get_next(); |
| while (entry != nullptr && entry->get_key() != key) { |
| prev = entry; |
| entry = entry->get_next(); |
| } |
| if (LIKELY(entry == nullptr)) { |
| void* buf = mem_alloc(sizeof(HashNode<KeyType, ValueType>), |
| MOD_HASH_TABLE); |
| if (UNLIKELY(nullptr == buf)) { |
| // log_err("malloc() failed."); |
| return E_OOM; |
| } |
| entry = new (buf) HashNode<KeyType, ValueType>; |
| entry->set_key(key); |
| entry->set_value(value); |
| entry->set_valid(true); |
| entry->set_rehashid(ATOMIC_LOAD(&hash_version_)); |
| prev->set_next(entry); |
| ATOMIC_AAF(&size_, 1); |
| } else { |
| exist = true; |
| value = entry->get_value(); |
| entry->set_rehashid(ATOMIC_LOAD(&hash_version_)); |
| } |
| } |
| return E_OK; |
| } |
| |
| int get(const KeyType& key, ValueType& result_value) { |
| if (!ATOMIC_LOAD(&size_)) { |
| return E_NOT_EXIST; |
| } |
| int ret; |
| ValueType result1; |
| ValueType result2; |
| ValueType result3; |
| while (true) { |
| uint64_t hv1 = ATOMIC_LOAD(&hash_version_); |
| |
| if ((hv1 & (1UL << 63))) { |
| ret = get_by_bucket(key, result1, |
| ATOMIC_LOAD(&old_table_capacity_)); |
| if (ret == E_OK) { |
| result_value = result1; |
| return ret; |
| } |
| ret = get_by_bucket(key, result2, |
| ATOMIC_LOAD(&new_table_capacity_)); |
| if (ret == E_OK) { |
| result_value = result2; |
| return ret; |
| } |
| } else { // |
| ret = get_by_bucket(key, result3, |
| ATOMIC_LOAD(&new_table_capacity_)); |
| if (ret == E_OK) { |
| result_value = result3; |
| return ret; |
| } |
| } |
| |
| uint64_t hv2 = ATOMIC_LOAD(&hash_version_); |
| if ((hv1 & (~(1UL << 63))) != (hv2 & (~(1UL << 63)))) { |
| continue; |
| } else if (ATOMIC_LOAD(&is_extending_)) { |
| continue; |
| } else { |
| return E_NOT_EXIST; |
| } |
| } |
| } |
| |
| int get_by_bucket(const KeyType& key, ValueType& result_value, |
| size_t table_capacity) { |
| uint32_t hash_value = hashfunc_(key); |
| size_t segment_idx = hash_value % table_capacity; |
| size_t bucket_idx = hash_value % SEGMENT_CAPACITY; |
| |
| table_[segment_idx]->mutexes_[bucket_idx].lock(); |
| |
| if (!((*table_[segment_idx])[bucket_idx]) |
| .get_valid()) { // if current segment has no node |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| return E_NOT_EXIST; |
| } else if (((*table_[segment_idx])[bucket_idx]).get_key() == key) { |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| result_value = ((*table_[segment_idx])[bucket_idx]).get_value(); |
| return E_OK; |
| } else { |
| HashNode<KeyType, ValueType>* entry = |
| ((*table_[segment_idx])[bucket_idx]).get_next(); |
| while (entry != nullptr) { |
| if (entry->get_key() == key) { |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| result_value = entry->get_value(); |
| return E_OK; |
| } |
| entry = entry->get_next(); |
| } |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| return E_NOT_EXIST; |
| } |
| } |
| |
| int remove(const KeyType& key) { |
| if (!ATOMIC_LOAD(&size_)) { |
| return E_NOT_EXIST; |
| } |
| int ret; |
| while (true) { |
| uint64_t hv1 = ATOMIC_LOAD(&hash_version_); |
| |
| if ((hv1 & (1UL << 63))) { |
| ret = remove_by_bucket(key, ATOMIC_LOAD(&old_table_capacity_)); |
| if (ret == E_OK) { |
| return ret; |
| } |
| ret = remove_by_bucket(key, ATOMIC_LOAD(&new_table_capacity_)); |
| if (ret == E_OK) { |
| return ret; |
| } |
| } else { |
| ret = remove_by_bucket(key, ATOMIC_LOAD(&new_table_capacity_)); |
| if (ret == E_OK) { |
| return ret; |
| } |
| } |
| |
| uint64_t hv2 = ATOMIC_LOAD(&hash_version_); |
| if ((hv1 & (~(1UL << 63))) != (hv2 & (~(1UL << 63)))) { |
| continue; |
| } else if (ATOMIC_LOAD(&is_extending_)) { |
| continue; |
| } else { |
| return E_NOT_EXIST; |
| } |
| } |
| } |
| |
| int remove_by_bucket(const KeyType& key, size_t table_capacity) { |
| uint32_t hash_value = hashfunc_(key); |
| size_t segment_idx = hash_value % table_capacity; |
| size_t bucket_idx = hash_value % SEGMENT_CAPACITY; |
| |
| table_[segment_idx]->mutexes_[bucket_idx].lock(); |
| |
| if (!((*table_[segment_idx])[bucket_idx]) |
| .get_valid()) { // if current segment has no node |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| return E_NOT_EXIST; |
| } else if (((*table_[segment_idx])[bucket_idx]).get_key() == key) { |
| HashNode<KeyType, ValueType>* entry = |
| ((*table_[segment_idx])[bucket_idx]).get_next(); |
| if (entry != nullptr) { |
| ((*table_[segment_idx])[bucket_idx]).set_key(entry->get_key()); |
| ((*table_[segment_idx])[bucket_idx]) |
| .set_value(entry->get_value()); |
| ((*table_[segment_idx])[bucket_idx]) |
| .set_next(entry->get_next()); |
| ((*table_[segment_idx])[bucket_idx]).set_valid(true); |
| ((*table_[segment_idx])[bucket_idx]) |
| .set_rehashid(entry->get_rehashid()); |
| entry->~HashNode(); |
| mem_free(entry); |
| entry = nullptr; |
| } else { |
| ((*table_[segment_idx])[bucket_idx]).set_valid(false); |
| } |
| ATOMIC_AAF(&size_, -1); |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| return E_OK; |
| } else { |
| HashNode<KeyType, ValueType>* prev = |
| &((*table_[segment_idx])[bucket_idx]); |
| HashNode<KeyType, ValueType>* entry = |
| ((*table_[segment_idx])[bucket_idx]).get_next(); |
| while (entry != nullptr) { |
| if (entry->get_key() == key) { |
| prev->set_next( |
| entry->get_next()); // remove node and reconnect |
| entry->~HashNode(); |
| mem_free(entry); |
| entry = nullptr; |
| ATOMIC_AAF(&size_, -1); |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| return E_OK; |
| } |
| prev = entry; |
| entry = entry->get_next(); |
| } |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| return E_NOT_EXIST; |
| } |
| } |
| |
| int remove_from_wrong_bucket(const KeyType& key, size_t segment_idx, |
| size_t bucket_idx) { |
| if (!ATOMIC_LOAD(&size_)) { |
| return E_NO_MORE_DATA; |
| } |
| |
| table_[segment_idx]->mutexes_[bucket_idx].lock(); |
| |
| if (!((*table_[segment_idx])[bucket_idx]) |
| .get_valid()) { // if current segment has no node |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| return E_NOT_EXIST; |
| } else if (((*table_[segment_idx])[bucket_idx]).get_key() == key) { |
| HashNode<KeyType, ValueType>* entry = |
| ((*table_[segment_idx])[bucket_idx]).get_next(); |
| if (entry != nullptr) { |
| ((*table_[segment_idx])[bucket_idx]).set_key(entry->get_key()); |
| ((*table_[segment_idx])[bucket_idx]) |
| .set_value(entry->get_value()); |
| ((*table_[segment_idx])[bucket_idx]) |
| .set_next(entry->get_next()); |
| ((*table_[segment_idx])[bucket_idx]).set_valid(true); |
| ((*table_[segment_idx])[bucket_idx]) |
| .set_rehashid(entry->get_rehashid()); |
| entry->~HashNode(); |
| mem_free(entry); |
| entry = nullptr; |
| } else { |
| ((*table_[segment_idx])[bucket_idx]).set_valid(false); |
| } |
| ATOMIC_AAF(&size_, -1); |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| return E_OK; |
| } else { |
| HashNode<KeyType, ValueType>* prev = |
| &((*table_[segment_idx])[bucket_idx]); |
| HashNode<KeyType, ValueType>* entry = |
| ((*table_[segment_idx])[bucket_idx]).get_next(); |
| while (entry != nullptr) { |
| if (entry->get_key() == key) { |
| prev->set_next( |
| entry->get_next()); // remove node and reconnect |
| entry->~HashNode(); |
| mem_free(entry); |
| entry = nullptr; |
| ATOMIC_AAF(&size_, -1); |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| return E_OK; |
| } |
| prev = entry; |
| entry = entry->get_next(); |
| } |
| table_[segment_idx]->mutexes_[bucket_idx].unlock(); |
| return E_NOT_EXIST; |
| } |
| } |
| |
| #ifndef NDEBUG |
| void print() { |
| printf("<< The Hash Table >>\n"); |
| if (!ATOMIC_AAF(&size_, 0)) { |
| printf(" Empty now!\n"); |
| return; |
| } |
| size_t bucket_num = 0; |
| for (size_t i = 0; i < ATOMIC_AAF(&new_table_capacity_, 0); i++) { |
| for (size_t j = 0; j < SEGMENT_CAPACITY; j++) { |
| // printf("----(%ld)segment id is %ld, bucket id is %ld----\n", |
| // bucket_num++, i, j); |
| if ((*table_[i])[j].get_valid()) { |
| printf("----(%ld)segment id is %ld, bucket id is %ld----\n", |
| bucket_num++, i, j); |
| std::cout << "| " << "key is " |
| << (*table_[i])[j].get_key() << ", value is " |
| << (*table_[i])[j].get_value() << ", rehashid is " |
| << (int)(*table_[i])[j].get_rehashid() |
| << std::endl; |
| HashNode<KeyType, ValueType>* entry = |
| ((*table_[i])[j]).get_next(); |
| while (entry != nullptr) { |
| std::cout << "| " << "key is " << entry->get_key() |
| << ", value is " << entry->get_value() |
| << ", rehashid is " |
| << (int)entry->get_rehashid() << std::endl; |
| entry = entry->get_next(); |
| } |
| } else { |
| bucket_num++; |
| } |
| } |
| } |
| } |
| #endif // #ifndef NDEBUG |
| |
| FORCE_INLINE bool empty() const { return ATOMIC_LOAD(&size_) == 0; } |
| |
| FORCE_INLINE size_t size() { return ATOMIC_LOAD(&size_); } |
| |
| FORCE_INLINE size_t total_capacity() { |
| return ATOMIC_LOAD(&new_table_capacity_) * SEGMENT_CAPACITY; |
| } |
| |
| private: |
| size_t new_table_capacity_; |
| size_t old_table_capacity_; |
| size_t size_; |
| uint64_t hash_version_; |
| Array<HashSegm<KeyType, ValueType>*> |
| table_; // the element in Array is pointer. |
| bool is_inited_; |
| uint8_t is_extending_; |
| F hashfunc_; |
| }; |
| |
| } // end namespace common |
| #endif // COMMON_CONTAINER_HASH_TABLE_H |