|
|
|
オープンアドレス型ハッシュ: 超基本編ソースはGitHubに移行しました。 Open-Addressed Hash Table (オープンアドレスハッシュテーブル)
目先をかえてOpen-Addressedハッシュを実装する。これは次のCuckooハッシュの下準備である。 ソースはGitHubに移行しました。 なお、通常のアルゴリズム解説ならば必須の、ハッシュ関数に関する議論は一切行わない。 ここではハッシュ関数hashCode()を"keyをテーブルサイズで割った余り"で定義する。
static unsigned int hashCode(lkey_t key, const hashtable_t * ht)
{
return (key % ht->table_size);
}
データ構造データ構造node_tとhashtable_tを定義する。 以降、共通のデータ型として"common.h"に"bool_t"、"lkey_t"、"val_t"を定義する。 "bool_t"は自明、 "lkey_t"はnodeのkey、"val_t"はnodeのvalを保持する。 common.h: typedef bool bool_t; typedef uint64_t lkey_t; typedef int64_t val_t; 次に、nodeとおよびhashtableのデータ構造を示す。
OpenAddressedHash.h
typedef enum {EMP = 0, DEL = 1, OCC = 2} node_stat;
typedef struct _node_t
{
lkey_t key; /* key */
val_t value; /* 値 */
node_stat stat; /* ノードの状態 */
} node_t;
typedef struct _hashtable_t
{
unsigned long int setSize; /* 全node数 */
node_t *bucket; /* ハッシュテーブル本体 */
unsigned int table_size; /* ハッシュテーブルの大きさ(長さ) */
node_t *old_bucket; /* resizeする際に、一時的に利用するハッシュテーブル */
unsigned int old_table_size; /* old_bucketの大きさ = resize前のテーブルの大きさ */
pthread_mutex_t mtx;
} hashtable_t;
node_tは(key,val)のペアを保持する。
hashtable_tには全ノード数を記録するsetSize、
ハッシュテーブル本体であるbucketとその大きさを記録するtable_size、
およびresize()でテーブルサイズを変更する際に、一時的にこれまでのテーブル(へのポインタ)を保持する
old_bucketとそのサイズを記録するold_table_sizeがある。 基本関数ハッシュテーブルの生成
/*
* bool_t init_bucket(hashtable_t * ht, const unsigned int table_size)
*
* ハッシュテーブルhtの各要素を初期化する。
*
* 成功すればtrue、失敗すればfalseを返す。
*/
static bool_t init_bucket(hashtable_t * ht, const unsigned int table_size)
{
unsigned int i;
if ((ht->bucket =
(node_t *) calloc(table_size, sizeof(node_t))) == NULL) {
elog("calloc error");
return false;
}
for (i = 0; i < table_size; i++)
set_node(&ht->bucket[i], (lkey_t) NULL, (val_t) NULL, EMP);
ht->setSize = 0;
return true;
}
static void free_bucket(node_t * bucket)
{
free(bucket);
}
/*
* hashtable_t *init_hashtable(const unsigned int table_size)
*
* 大きさtable_sizeのハッシュテーブルを生成する。
*
* 成功すれば、そのポインタを返す。失敗すればNULLを返す。
*
*/
hashtable_t *init_hashtable(const unsigned int size)
{
hashtable_t *ht;
unsigned int table_size = (0x0001 << size);
if ((ht = (hashtable_t *) calloc(1, sizeof(hashtable_t))) == NULL) {
elog("calloc error");
return NULL;
}
ht->table_size = table_size;
ht->mtx = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
if (init_bucket(ht, table_size) != true) {
free(ht);
return NULL;
}
return ht;
}
要素の書き込み
static void
set_node(node_t * node, const lkey_t key, const val_t val,
const node_stat stat)
{
assert(node != NULL);
node->key = key;
node->value = val;
node->stat = stat;
}
static void
add_op(hashtable_t * ht, node_t * node, const lkey_t key,
const val_t val)
{
set_node(node, key, val, OCC);
ht->setSize++;
}
/*
* bool_t add(hashtable_t * ht, const lkey_t key, const val_t val)
*
* ハッシュテーブルhtに要素(key,val)を書き込む。
*
* 成功すればtrue、失敗すればfalseを返す。
*/
bool_t add(hashtable_t * ht, const lkey_t key, const val_t val)
{
unsigned int i, myBucket;
node_t *node;
bool_t ret = false;
lock(ht->mtx);
for (i = 0; i < ht->table_size; i++) {
myBucket = hashCode(key, i, ht);
node = &ht->bucket[myBucket];
if (node->stat != OCC) {
add_op(ht, node, key, val);
ret = true;
break;
}
}
if (policy(ht)) {
resize(ht);
fprintf (stderr, "Resized\n");
}
unlock(ht->mtx);
return ret;
}
要素の削除オープンアドレスなので、論理的な削除である。
static void del_op(hashtable_t * ht, node_t * node)
{
set_node(node, (lkey_t) NULL, (val_t) NULL, DEL);
ht->setSize--;
}
/*
* bool_t delete(hashtable_t * ht, const lkey_t key, val_t * getval)
*
* ハッシュテーブルhtからkeyを持つ要素を削除し、そのvalを*valに書き込む。
* keyが存在しない場合は失敗。
*
* 成功すればtrue、失敗すればfalseを返す。
*/
bool_t delete(hashtable_t * ht, const lkey_t key, val_t * getval)
{
unsigned int i, myBucket;
node_t *node;
bool_t ret = false;
lock(ht->mtx);
for (i = 0; i < ht->table_size; i++) {
myBucket = hashCode(key, i, ht);
node = &ht->bucket[myBucket];
if (node->stat == EMP) {
ret = false;
break;
} else if (node->stat != DEL && node->key == key) {
ret = true;
*getval = node->value;
del_op(ht, node);
break;
}
}
unlock(ht->mtx);
return ret;
}
要素の検索
/*
* bool_t find(hashtable_t * ht, const lkey_t key)
*
* ハッシュテーブルhtからkeyを持つ要素を検索し、そのvalを*valに書き込む。
* keyが存在しない場合は失敗。
*
* 成功すればtrue、失敗すればfalseを返す。
*/
bool_t find(hashtable_t * ht, const lkey_t key)
{
unsigned int i, myBucket;
node_t *node;
bool_t ret = false;
lock(ht->mtx);
for (i = 0; i < ht->table_size; i++) {
myBucket = hashCode(key, i, ht);
node = &ht->bucket[myBucket];
if (node->stat == EMP) {
ret = false;
break;
} else if (node->stat != DEL && node->key == key) {
ret = true;
break;
}
}
unlock(ht->mtx);
return ret;
}
ハッシュテーブルの再構築(resize())
static bool_t policy(hashtable_t * ht)
{
// return ((ht->table_size * 3 / 4) < ht->setSize ? true : false);
return ((ht->table_size * 4 / 5) < ht->setSize ? true : false);
}
static void resize(hashtable_t * ht)
{
node_t *old_node, *new_node;
unsigned int i, j, myBucket;
ht->old_table_size = ht->table_size;
ht->old_bucket = ht->bucket;
if (init_bucket(ht, (ht->table_size * 2)) == false)
return;
ht->table_size *= 2;
for (i = 0; i < ht->old_table_size; i++) {
old_node = &ht->old_bucket[i];
if (old_node->stat == OCC) {
for (j = 0; j < ht->table_size; j++) {
myBucket = hashCode(old_node->key, j, ht);
new_node = &ht->bucket[myBucket];
if (new_node->stat != OCC) {
add_op(ht, new_node, old_node->key, old_node->value);
break;
}
}
}
}
free_bucket(ht->old_bucket);
}
実行ソースはGitHubに移行しました。
Last-modified: 2014-7-6
|