polardbxengine/storage/xengine/core/util/hashtable.h

228 lines
6.0 KiB
C++

/*
* Copyright (c) 2020, Alibaba Group Holding Limited
* Licensed 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.
*/
#pragma once
#include "logger/logger.h"
#include "util/misc_utility.h"
#include "util/fast_latch.h"
namespace xengine
{
namespace util
{
template<class ValTp>
struct HashTableNode
{
HashTableNode():
next_(nullptr),
data_(),
hash_(0) {}
HashTableNode *next_;
ValTp data_;
uint64_t hash_;
};
template <class ValTp, class LockTp = port::RWMutex>
struct HashTableBucket
{
LockTp mu_;
HashTableNode<ValTp> *node_;
};
template<class KeyTp,
class ValTp,
class HashFunc,
class GetKey,
class CheckFunc,
class CheckType,
class KeyEqual = std::less<KeyTp>>
class HashTable
{
public:
const static int64_t DEFAULT_LENGTH = 1024;
const static int64_t SHARD_LOCKS_NUM = DEFAULT_LENGTH;
typedef size_t size_type;
typedef HashTableNode<ValTp> hashnode;
typedef HashTableBucket<ValTp> hashbucket;
public:
HashTable(const int64_t bucket_num = DEFAULT_LENGTH)
: buckets_(nullptr),
num_elements_(0),
bucket_num_(bucket_num),
init_(false),
locks_(nullptr),
nodes_(nullptr) {
usage_ = DEFAULT_LENGTH * (sizeof(hashnode *) + sizeof(FastRWLatch) + sizeof(hashnode));
}
~HashTable() {
destroy();
}
void set_buckets_num(const int64_t num) {
bucket_num_ = num;
usage_ = bucket_num_ * (sizeof(hashnode *) + sizeof(FastRWLatch) + sizeof(hashnode));
}
void destroy() {
clear();
}
void clear() {
if (init_) {
for (int64_t i = 0; i < bucket_num_; ++i) {
FastWRGuard l(&locks_[i]);
hashnode *node = buckets_[i];
while (nullptr != node) {
hashnode *nextnode = node->next_;
if (node != &nodes_[i]) {
MOD_DELETE_OBJECT(hashnode, node);
}
node = nextnode;
}
}
num_elements_ = 0;
memory::base_free(buckets_);
memory::base_free(nodes_);
memory::base_free(locks_);
}
}
hashnode **find_node_ptr(const KeyTp &key, int64_t idx, uint64_t hash) {
hashnode **node = &buckets_[idx];
while (nullptr != *node && ((*node)->hash_ != hash || !equal_(key, get_key_((*node)->data_)))) {
node = &(*node)->next_;
}
return node;
}
int insert(const KeyTp &key, const ValTp &val, const CheckType check, ValTp &old_val) {
int ret = 0;
if (!init_) {
construct();
}
int len = 0;
uint64_t hash = hashfunc_(key);
int64_t bucket_idx = hash & (bucket_num_ - 1);
FastWRGuard l(&locks_[bucket_idx]);
if (!check_func_(check)) {
ret = common::Status::kInsertCheckFailed;
return ret;
}
hashnode **node = find_node_ptr(key, bucket_idx, hash);
hashnode *old = *node;
if (nullptr == old) {
hashnode *new_node = (nullptr == buckets_[bucket_idx])
? &nodes_[bucket_idx]
: MOD_NEW_OBJECT(memory::ModId::kCacheHashTable, hashnode);
new_node->data_ = val;
new_node->hash_ = hash;
new_node->next_ = nullptr;
*node = new_node;
++num_elements_;
} else {
old_val = old->data_;
old->data_ = val;
}
return ret;
}
int remove(const KeyTp &key, ValTp &val) {
int ret = 0;
if (!init_) {
construct();
val = nullptr;
return ret;
}
uint64_t hash = hashfunc_(key);
int64_t bucket_idx = hash & (bucket_num_ - 1);
FastWRGuard l(&locks_[bucket_idx]);
hashnode **node = find_node_ptr(key, bucket_idx, hash);
hashnode *result = *node;
if (nullptr != result) {
*node = result->next_;
--num_elements_;
val = result->data_;
if (result != &nodes_[bucket_idx]) {
MOD_DELETE_OBJECT(hashnode, result);
}
} else {
val = nullptr;
}
return ret;
}
int find(const KeyTp &key, ValTp &val, bool need_ref = false) {
int ret = 0;
if (!init_) {
construct();
return ret;
}
uint64_t hash = hashfunc_(key);
int64_t bucket_idx = hash & (bucket_num_ - 1);
FastRDGuard l(&locks_[bucket_idx]);
hashnode *node = *find_node_ptr(key, bucket_idx, hash);
if (nullptr != node) {
val = node->data_;
if (need_ref) {
val->ref();
}
}
return ret;
}
int construct() {
int ret = 0;
MutexLock l(&mutex_);
if (!init_) {
buckets_ = (hashnode **)memory::base_malloc(bucket_num_ * sizeof(hashnode *), memory::ModId::kCacheHashTable);
memset(buckets_, 0, sizeof(hashnode *) * bucket_num_);
int64_t locks_size = (bucket_num_) * sizeof(FastRWLatch);
locks_ = (FastRWLatch *)memory::base_malloc(locks_size, memory::ModId::kCacheHashTable);
memset(locks_, 0, locks_size);
nodes_ = (hashnode *)memory::base_malloc(bucket_num_ * sizeof(hashnode), memory::ModId::kCacheHashTable);
memset(nodes_, 0, bucket_num_ * sizeof(hashnode));
init_ = true;
}
return ret;
}
size_type bucket_count() const {
return bucket_num_;
}
size_type elems_in_bucket(size_type bucket_idx) {
size_type result = 0;
if (bucket_idx >= bucket_num_) {
return result;
}
for (hashnode* cur = buckets_[bucket_idx]; cur; cur = cur->next) {
++result;
}
return result;
}
bool empty() const {
return 0 == num_elements_;
}
int64_t get_usage() const {
return usage_;
}
private:
hashnode **buckets_;
int64_t num_elements_;
int64_t bucket_num_;
bool init_;
FastRWLatch *locks_;
hashnode *nodes_;
int64_t usage_;
mutable port::Mutex mutex_;
mutable HashFunc hashfunc_;
mutable KeyEqual equal_;
GetKey get_key_;
CheckFunc check_func_;
};
}
}