ハッシュマップC実装
2022-02-19 16:04:24
ハッシュマップC実装
チョンマイン
ソースコード(Linux版、Windows版)が含まれます。
hashmap.c
hashmap.h
MSVCのテストファイルです。
main.c
以下はソースコードで、元々はgithubにあったものですが、数カ所を書き直し、テストコードも全て書き直しました。メモリリークはありませんので、ご自由にお使いください。
/**
* hashmap.h
*/
#ifndef _HASHMAP_H_INCLUDED
#define _HASHMAP_H_INCLUDED
#if defined(__cplusplus)
extern "C" {
#endif
#define HMAP_E_KEYUSED (-5) /* Key already existed */
#define HMAP_E_OUTMEM (-4) /* Out of Memory */
#define HMAP_E_NOTFOUND (-3) /* No such element */
#define HMAP_E_OVERFLOW (-2) /* Hashmap is full */
#define HMAP_E_FAIL (-1) /* Hashmap api fail */
#define HMAP_S_OK (0) /* Success */
/**
** void_ptr is a pointer. This allows you to put arbitrary structures in the hashmap.
*/
typedef void* void_ptr;
/**
* hmap_t is a pointer to an internally maintained data structure.
* Clients of this package do not need to know how hashmaps are
They see and manipulate only hmap_t's. */ /* Clients of this package do not need to know how hashmaps are represented.
They see and manipulate only hmap_t's. */
typedef void_ptr hmap_t;
/**
* hmap_callback_func is a pointer to a function that can take two void_ptr arguments
* Returns status code.
*/
typedef int (*hmap_callback_func)(void_ptr, void_ptr);
/**
* prototype for map element type
*/
typedef struct _hmap_pair_t {
char *key;
void_ptr data;
} hmap_pair_t;
/**
* Return an empty hashmap. returns NULL if empty.
*/
extern hmap_t hashmap_create();
/**
* Iteratively call fn with argument (value, arg) for each element data * in the hashmap.
* The function returns anything other than HMAP_S_OK
The function returns anything other than HMAP_S_OK. * the traversal is terminated. fn must not modify any hashmap functions.
*/
extern int hashmap_iterate(hmap_t in, hmap_callback_func fnIterValue, void_ptr arg);
/**
* Add an element to the hashmap.
* Return HMAP_S_OK, HMAP_E_KEYUSED or HMAP_E_OUTMEM.
*/
extern int hashmap_put(hmap_t in, char* key, void_ptr elem);
/**
* Get an element from the hashmap. return HMAP_S_OK or HMAP_E_NOTFOUND.
*/
extern int hashmap_get(hmap_t in, const char* key, void_ptr *elem);
/**
* Remove an element from the hashmap. return HMAP_S_OK or HMAP_E_NOTFOUND.
*/
extern int hashmap_remove(hmap_t in, char* key, void_ptr *outValue);
/**
* Free the hashmap
*/
extern void hashmap_destroy(hmap_t in, hmap_callback_func fnFreeValue, void_ptr arg);
/**
* Get the current size of a hashmap
*/
extern int hashmap_size(hmap_t in);
#if defined(__cplusplus)
}
#endif
#endif /* _HASHMAP_H_INCLUDED */
/**
* hashmap.c
* Generic hash map implementation.
*/
#include "hashmap.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define HMAP_INITIAL_SIZE (256)
#define HMAP_CHAIN_LENGTH (8)
typedef enum _use_state {
unused_0 = 0,
used_1 = 1
} use_state;
/* A element to keep keys and values */
typedef struct _hashmap_elem_t{
char *key; /* pointer to actual key storage */
use_state used; /* unused_0, used_1 */
void_ptr data; /* pointer to value memory allocated by callee */
} hashmap_elem_t;
/* A hashmap has maximum size and current size, as well as the elems to hold */
typedef struct _hashmap_map_t{
int table_size;
int size;
hashmap_elem_t *elems;
} hashmap_map_t;
/**
* The implementation here was originally done by Gary S. Brown.
* I have borrowed the tables directly, and made some minor changes.
*I have borrowed the tables directly, and made some minor changes.
* COPYRIGHT (C) 1986 Gary S. Brow
/* Knuth's Multiplicative Method */
key = (key >> 3) * 0x9E3779B1;
return key % m-> table_size;
}
/**
* Return the integer of the location in data to store the point to the item,
* or HMAP_E_OVERFLOW.
*/
static int _hashmap_hash(hmap_t in, char* key){
int curr;
int i;
hashmap_elem_t *elem;
hashmap_map_t *m = (hashmap_map_t *) in;
/* If full, return immediately */
if (m->size >= (m->table_size/2)) {
return HMAP_E_OVERFLOW;
}
/* Find the best index */
curr = _find_hash_index(m, key);
/* Linear probing */
for (i = 0; i< HMAP_CHAIN_LENGTH; i++) {
elem = m-> elems + curr;
if(elem->used == unused_0) {
return curr;
}
if(elem->used == used_1 && (!strcmp(elem->key, key))) {
return curr;
}
curr = (curr + 1) % m->table_size;
}
return HMAP_E_OVERFLOW;
}
/**
* Doubles the size of the hashmap, and rehashes all the elements
*/
static int _hashmap_rehash(hmap_t in){
int i;
int old_size;
hashmap_elem_t *curr;
hashmap_elem_t *elem;
/* Setup the new elements */
hashmap_map_t *m = (hashmap_map_t *) in;
hashmap_elem_t *temp = (hashmap_elem_t *) calloc(2 * m->table_size, sizeof(hashmap_elem_t));
if (!temp) {
return HMAP_E_OUTMEM;
}
/* Update the array */
curr = m->elems;
m->elems = temp;
/* Update the size */
old_size = m->table_size;
m->table_size = 2 * m->table_size;
m->size = 0;
/* Rehash the elements */
for (i = 0; i < old_size; i++){
int status;
elem = curr + i;
if (elem->used == unused_0) {
continue;
}
status = hashmap_put(m, elem->key, elem->data);
if (status ! = HMAP_S_OK) {
return status;
}
}
free(curr);
return HMAP_S_OK;
}
/**
* Create an empty hashmap
*/
hmap_t hashmap_create() {
hashmap_map_t* m = (hashmap_map_t*) malloc(sizeof(hashmap_map_t));
if (!m) {
exit(HMAP_E_OUTMEM);
}
m->elems = (hashmap_elem_t*) calloc(HMAP_INITIAL_SIZE, sizeof(hashmap_elem_t));
if (!m->elems) {
free(m);
exit(HMAP_E_OUTMEM);
}
m->table_size = HMAP_INITIAL_SIZE;
m->size = 0;
return m;
}
/**
* Add a pair of key-value to the hashmap
*/
int hashmap_put(hmap_t in, char* key, void_ptr value){
int index;
hashmap_map_t *m;
hashmap_elem_t *elem;
m = (hashmap_map_t *) in;
/* Find a place to put our value */
index = _hashmap_hash(in, key);
while (index == HMAP_E_OVERFLOW) {
if (_hashmap_rehash(in) == HMAP_E_OUTMEM) { if (_hashmap_rehash(in) == HMAP_E_OUTMEM)
return HMAP_E_OUTMEM;
}
index = _hashmap_hash(in, key);
}
/* Set the elems */
elem = m->elems + index;
if (elem->used == used_1) {
/* Find a repeated key */
return HMAP_E_KEYUSED;
}
elem->data = value;
elem->key = key; /* only set to a reference */
elem->used = used_1;
m->size++;
return HMAP_S_OK;
}
/**
* Get your pointer out of the hashmap with a key
*/
int hashmap_get(hmap_t in, const char* key, void_ptr *value){
int curr;
int i;
hashmap_map_t *m;
hashmap_elem_t *elem;
m = (hashmap_map_t *) in;
/* Find element location */
curr = _find_hash_index(m, key);
/* Linear probing, if necessary */
for (i = 0; i<HMAP_CHAIN_LENGTH; i++){
elem = m->elems + curr;
if (elem->used == used_1) {
if (!strcmp(elem->key, key)) {
*value = (elem->data);
return HMAP_S_OK;
}
}
curr = (curr + 1) % m->table_size;
}
*value = NULL;
return HMAP_E_NOTFOUND;
}
/**
* Iterate the function parameter over each eleme
/**
* main.c
*
* Detecting memory leaks only for windows .
* Place the following snippet where leak to be tested:
* #if defined(_CRTDBG_MAP_ALLOC)
* _CrtDumpMemoryLeaks();
* #endif
*/
#if defined(WIN32) && defined(_DEBUG)
#ifndef _CRTDBG_MAP_ALLOC
#pragma message( __FILE__": _CRTDBG_MAP_ALLOC defined only for DEBUG on Win32." )
#define _CRTDBG_MAP_ALLOC
#include<stdlib.h>
#include<crtdbg.h>
#endif
#endif
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "hashmap.h"
typedef struct userelem_t {
char key[20];
char *value;
} userelem;
typedef struct userdata_t {
char name[20];
hmap_t map; /* userelem map */
} userdata;
static int iter_elem(void* elem, void *arg) {
userelem *el = (userelem *) elem;
printf("key=%s; value=%s\n", el->key, el->value);
return 0;
}
static int free_elem(void* elem, void *arg) {
userelem *el = (userelem *) elem;
free(el-> value);
free(el);
return 0;
}
static int free_data(void* data, void *arg) {
userdata *dat = (userdata *) data;
/* delete the entire submap */
hashmap_destroy(dat-> map, free_elem, 0);
free(dat);
return 0;
}
int main(int argc, char* argv[])
{
hmap_t map;
userdata *dat;
userelem *el;
int ret, i, j;
/* create hashmap */
map = hashmap_create();
/* insert hashmap element */
for (i=0; i<100; i++) {
dat = (userdata *)malloc(sizeof(userdata));
/* create sub hashmap */
dat->map = hashmap_create();
/* insert sub hashmap elements */
for (j=0; j<10; j++) {
el = (userelem *)malloc(sizeof(userelem));
sprintf(el->key, "%d", j);
el->value = (char*) malloc(30);
sprintf(el->value, "%d", j+1000);
ret = hashmap_put(date-> map, el-> key, el);
assert(ret==HMAP_S_OK);
}
sprintf(dat->name, "%d", i);
ret = hashmap_put(map, dat->name, dat);
assert(ret==HMAP_S_OK);
}
printf("hashmap_size: %d\n", hashmap_size(map));
/* Remove the specified element: key="10" */
ret = hashmap_remove(map, "10", &dat);
assert(ret==HMAP_S_OK);
printf("hashmap_remove: name=%s. size=%d\n", dat->name, hashmap_size(map));
hashmap_iterate(dat->map, iter_elem, 0);
free_data(dat, 0);
/* Remove the specified element: key="11" */
ret = hashmap_remove(map, "11", &dat);
assert(ret==HMAP_S_OK);
printf("hashmap_remove: name=%s. size=%d\n", dat->name, hashmap_size(map));
hashmap_iterate(dat->map, iter_elem, 0);
free_data(dat, 0);
/* Query element: key="99" */
ret = hashmap_get(map, "99", &dat);
assert(ret==HMAP_S_OK);
printf("hashmap_get: name=%s. size=%d\n", dat->name, hashmap_size(map));
hashmap_iterate(date->map, iter_elem, 0);
/* delete the entire map */
hashmap_destroy(map, free_data, 0);
_CrtDumpMemoryLeaks();
return 0;
}
/**
* main.c
*
* Detecting memory leaks only for windows .
* Place the following snippet where leak to be tested:
* #if defined(_CRTDBG_MAP_ALLOC)
* _CrtDumpMemoryLeaks();
* #endif
*/
#if defined(WIN32) && defined(_DEBUG)
#ifndef _CRTDBG_MAP_ALLOC
#pragma message( __FILE__": _CRTDBG_MAP_ALLOC defined only for DEBUG on Win32." )
#define _CRTDBG_MAP_ALLOC
#include<stdlib.h>
#include<crtdbg.h>
#endif
#endif
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "hashmap.h"
typedef struct userelem_t {
char key[20];
char *value;
} userelem;
typedef struct userdata_t {
char name[20];
hmap_t map; /* userelem map */
} userdata;
static int iter_elem(void* elem, void *arg) {
userelem *el = (userelem *) elem;
printf("key=%s; value=%s\n", el->key, el->value);
return 0;
}
static int free_elem(void* elem, void *arg) {
userelem *el = (userelem *) elem;
free(el-> value);
free(el);
return 0;
}
static int free_data(void* data, void *arg) {
userdata *dat = (userdata *) data;
/* delete the entire submap */
hashmap_destroy(dat-> map, free_elem, 0);
free(dat);
return 0;
}
int main(int argc, char* argv[])
{
hmap_t map;
userdata *dat;
userelem *el;
int ret, i, j;
/* create hashmap */
map = hashmap_create();
/* insert hashmap element */
for (i=0; i<100; i++) {
dat = (userdata *)malloc(sizeof(userdata));
/* create sub hashmap */
dat->map = hashmap_create();
/* insert sub hashmap elements */
for (j=0; j<10; j++) {
el = (userelem *)malloc(sizeof(userelem));
sprintf(el->key, "%d", j);
el->value = (char*) malloc(30);
sprintf(el->value, "%d", j+1000);
ret = hashmap_put(date-> map, el-> key, el);
assert(ret==HMAP_S_OK);
}
sprintf(dat->name, "%d", i);
ret = hashmap_put(map, dat->name, dat);
assert(ret==HMAP_S_OK);
}
printf("hashmap_size: %d\n", hashmap_size(map));
/* Remove the specified element: key="10" */
ret = hashmap_remove(map, "10", &dat);
assert(ret==HMAP_S_OK);
printf("hashmap_remove: name=%s. size=%d\n", dat->name, hashmap_size(map));
hashmap_iterate(dat->map, iter_elem, 0);
free_data(dat, 0);
/* Remove the specified element: key="11" */
ret = hashmap_remove(map, "11", &dat);
assert(ret==HMAP_S_OK);
printf("hashmap_remove: name=%s. size=%d\n", dat->name, hashmap_size(map));
hashmap_iterate(dat->map, iter_elem, 0);
free_data(dat, 0);
/* Query element: key="99" */
ret = hashmap_get(map, "99", &dat);
assert(ret==HMAP_S_OK);
printf("hashmap_get: name=%s. size=%d\n", dat->name, hashmap_size(map));
hashmap_iterate(date->map, iter_elem, 0);
/* delete the entire map */
hashmap_destroy(map, free_data, 0);
_CrtDumpMemoryLeaks();
return 0;
}
関連
-
[解決済み】警告。この関数ではXが初期化されていない状態で使用される可能性があります。
-
[解決済み】sizeof float (3.0) vs (3.0f)
-
[解決済み] C言語にとって.cと.hのファイル拡張子はどのような意味を持つのですか?
-
[解決済み] C言語でのULONG_MAXの宣言
-
[解決済み] ATMEGA168A - F_CPU 警告
-
[解決済み] 関数 'wait' の暗黙の宣言
-
[解決済み] MPEG-DASHのmpdで指定された.m4sファイルをプレーヤーで再生するにはどうしたらいいですか?
-
[解決済み] MPI_ScatterとMPI_GatherはC言語からどのように使用するのですか?
-
[解決済み] C言語におけるi = i&1とはどういう意味ですか?
-
[解決済み] strtolによるセグメンテーションの不具合
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】GCC Cコードで静的宣言が非静的宣言に続くことを解決するには?
-
[解決済み】デバッガgdbの使用時に不明な終了シグナルが発生する。
-
[解決済み] lseek/write が突然 errno = 9 (Bad file descriptor) で -1 を返した。
-
[解決済み] valgrind使用時のサイズ1の無効な読み取り
-
[解決済み] 改行文字を破棄しないstrtok
-
[解決済み] C言語ではINET6_ADDRSTRLENはなぜ46と定義されているのですか?
-
[解決済み] char 配列を空にするには?
-
[解決済み] sys/types.hはどこにありますか?
-
[解決済み] valgrind アドレス 0x421688c は、整数データを持つリンクリスト用に割り当てられたサイズ 4 のブロックの後で 0 バイトです。
-
[解決済み] 式はCのオブジェクト型へのポインタを持たなければならない