📄 hash.c
字号:
/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */#include <stdlib.h>#include <string.h>#include <assert.h>#include <stdio.h>#include "hash.h"static int __chose_table_size(int size){ static int ht_size[] = { 37, 47, 79, 97, 163, 197, 331, 397, 673, 797, 1361, 1597, 2729, 3203, 5471, 6421, 10949, 12853, 21911, 25717, 43853, 51437, 87719, 102877, 175447, 205759, 350899, 411527, 701819, 823117, 1403641, 1646237, 2807303, 3292489, 5614657, 6584983, 11229331, 13169977, 22458671, 26339969, 44917381, 52679969, 89834777, 105359939 }; int i; if (size >= ht_size[sizeof(ht_size) / sizeof(ht_size[0]) - 1]) return ht_size[sizeof(ht_size) / sizeof(ht_size[0])]; for (i = 0; ht_size[i] < size; i++) ; return ht_size[i];}static inline int __when_need_space(int max_items){ return (HASH_MAX_PCT_FULL * max_items) / 100;}static inline int __hash_need_space(struct hash *h){ return h->h_items >= h->h_space_after ? 1 : 0;// return (h->h_items * 100) / h->h_max_items >= HASH_MAX_PCT_FULL ? 1 : 0;}int hash_init(struct hash *h, int max_items, hash_equal_func eqfunc){ if (max_items < HASH_MIN_ITEMS) max_items = HASH_MIN_ITEMS; max_items = __chose_table_size(max_items); h->h_space_after = __when_need_space(max_items); h->h_items = 0; h->h_max_items = max_items; h->h_eqfunc = eqfunc; if (!(h->h_table = malloc(max_items * sizeof(struct hash_table_item)))) return -1; memset(h->h_table, 0, max_items * sizeof(struct hash_table_item));#ifdef _REENTRANT h->h_locked = 0; pthread_mutex_init(&h->h_mutex, NULL);#endif return 0;}void hash_free(struct hash *h){ if (h->h_table) free(h->h_table); memset(h, 0, sizeof(*h));}static unsigned int __hv_hash;static unsigned int __hv_hash_save;static unsigned int __hv_max_items;#ifdef HASH_FUNC_2static unsigned int __hv_d;static unsigned int __hv_d_save;#endifstatic inline int __hv(unsigned int key, unsigned int max_items){ __hv_max_items = max_items;#ifdef HASH_FUNC_2 __hv_d = 2;#endif return (int)(__hv_hash = key % max_items);}static inline int __hv_peek(void){ return __hv_hash;}static inline int __hv_next(void){#ifdef HASH_FUNC_2 __hv_hash = (__hv_hash + __hv_d) % __hv_max_items; __hv_d += 2; return (int)__hv_hash;#else return __hv_hash = (__hv_hash + 1) % __hv_max_items;#endif}static inline int __hv_peek_next(void){#ifdef HASH_FUNC2 return (int)((__hv_hash + __hv_d) % __hv_max_items);#else return (__hv_hash + 1) % __hv_max_items;#endif}static inline void __hv_save_position(void){#ifdef HASH_FUNC_2 __hv_d_save = __hv_d;#endif __hv_hash_save = __hv_hash;}static inline int __hv_load_position(void){#ifdef HASH_FUNC_2 __hv_d = __hv_d_save;#endif return (int)(__hv_hash = __hv_hash_save);}static int __hash_find(struct hash *h, unsigned int key, void *arg){ int retval; int hv; struct hash_table_item *h_table; hv = __hv(key, h->h_max_items); h_table = h->h_table; retval = -1; while (h_table[hv].ht_data) { if (h_table[hv].ht_key == key) { if (!h->h_eqfunc || h_table[__hv_peek_next()].ht_data == NULL) { retval = hv; break; } else { if (h->h_eqfunc(key, h_table[hv].ht_data, arg)) { retval = hv; break; } } } hv = __hv_next(); } return retval;}static void __hash_put(struct hash *h, unsigned int key, void *data){ int hv; struct hash_table_item *h_table; hv = __hv(key, h->h_max_items); h_table = h->h_table; while (h_table[hv].ht_data) { hv = __hv_next(); } h_table[hv].ht_key = key; h_table[hv].ht_data = data; h->h_items++;}static int __hash_put_check(struct hash *h, unsigned int key, void *data, void *arg){ int retval; int hv; struct hash_table_item *h_table = h->h_table; retval = __hash_find(h, key, arg); if (retval < 0) { hv = __hv_peek(); h_table[hv].ht_key = key; h_table[hv].ht_data = data; return 0; } else return -1;}static void __hash_remap(struct hash *h, int hv){ int count, i; struct hash_table_item *h_table, *ht_save; __hv_save_position(); h_table = h->h_table; count = 0; while (h_table[hv].ht_data) { count++; hv = __hv_next(); } if (count) { ht_save = alloca(count * sizeof(struct hash_table_item)); hv = __hv_load_position(); i = 0; while (h_table[hv].ht_data) { ht_save[i].ht_key = h_table[hv].ht_key; ht_save[i].ht_data = h_table[hv].ht_data; h_table[hv].ht_key = 0; h_table[hv].ht_data = NULL; i++; hv = __hv_next(); } h->h_items -= count; for (i = 0; i < count; i++) __hash_put(h, ht_save[i].ht_key, ht_save[i].ht_data); }}static void __hash_new_space(struct hash *h){ struct hash_table_item *old_h_table; int old_items, old_max_items; int i; old_items = h->h_items; old_max_items = h->h_max_items; old_h_table = h->h_table; h->h_max_items = __chose_table_size(h->h_max_items + (HASH_SPACE_PCT_INC * h->h_max_items) / 100); if (!(h->h_table = malloc(h->h_max_items * sizeof(struct hash_table_item)))) { h->h_items = old_items; h->h_max_items = old_max_items; h->h_table = old_h_table; return; } h->h_space_after = __when_need_space(h->h_max_items); memset(h->h_table, 0, h->h_max_items * sizeof(struct hash_table_item)); h->h_items = 0; for (i = 0; i < old_max_items; i++) { if (old_h_table[i].ht_data) __hash_put(h, old_h_table[i].ht_key, old_h_table[i].ht_data); } free(old_h_table);}static inline void __lock(struct hash *h){#ifdef _REENTRANT if (!h->h_locked || h->h_locked_thr != pthread_self()) pthread_mutex_lock(&h->h_mutex);#endif}static inline void __unlock(struct hash *h){#ifdef _REENTRANT if (!h->h_locked || h->h_locked_thr != pthread_self()) pthread_mutex_unlock(&h->h_mutex);#endif}void *hash_get(struct hash *h, unsigned int key, void *arg){ int hv; void *retval; __lock(h); if ((hv = __hash_find(h, key, arg)) >= 0) retval = h->h_table[hv].ht_data; else retval = NULL; __unlock(h); return retval;}void *hash_remove(struct hash *h, unsigned int key, void *arg){ void *retval; int hv; __lock(h); if ((hv = __hash_find(h, key, arg)) >= 0) { retval = h->h_table[hv].ht_data; h->h_table[hv].ht_key = 0; h->h_table[hv].ht_data = NULL; h->h_items--; __hash_remap(h, __hv_next()); } else retval = NULL; __unlock(h); return retval;}int hash_put(struct hash *h, unsigned int key, void *data){ int retval; __lock(h); if (__hash_need_space(h)) __hash_new_space(h); if (h->h_items == h->h_max_items || !data) retval = -1; else retval = 0; __hash_put(h, key, data); __unlock(h); return retval;}int hash_put_check(struct hash *h, unsigned int key, void *data, void *arg){ int retval; __lock(h); if (__hash_need_space(h)) __hash_new_space(h); if (h->h_items == h->h_max_items || !data) retval = -1; else retval = __hash_put_check(h, key, data, arg); __unlock(h); return retval;}int hash_count(struct hash *h){ int retval; __lock(h); retval = h->h_items; __unlock(h); return retval;}void hash_lock(struct hash *h){#ifdef _REENTRANT if (!h->h_locked || h->h_locked_thr != pthread_self()) { pthread_mutex_lock(&h->h_mutex); h->h_locked_thr = pthread_self(); h->h_locked = 1; } else h->h_locked++;#endif}void hash_unlock(struct hash *h){#ifdef _REENTRANT if (--h->h_locked == 0) pthread_mutex_unlock(&h->h_mutex);#endif}/* * * hash_iterator * */void hash_iter_set(struct hash_iterator *i, struct hash *h){ i->i_hash = h; i->i_pos = 0;}void hash_iter_lock(struct hash_iterator *i){ hash_lock(i->i_hash);}void hash_iter_unlock(struct hash_iterator *i){ hash_unlock(i->i_hash);}void *hash_iter_get(struct hash_iterator *i, unsigned int *keyptr){ struct hash *h; void *data; void *retval; h = i->i_hash; __lock(h); for ( ;i->i_pos < h->h_max_items; i->i_pos++) { data = h->h_table[i->i_pos].ht_data; if (data) break; } if (i->i_pos < h->h_max_items && data) { retval = data; if (keyptr) *keyptr = h->h_table[i->i_pos].ht_key; } else retval = NULL; i->i_pos++; __unlock(h); return retval;}void hash_iter_end(struct hash_iterator *i){ i->i_hash = NULL; i->i_pos = 0;}#ifdef TEST/* * * Test * */#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <unistd.h>#include <assert.h>#include <alloca.h>int MAX_ITEMS = 1000;static int verbose = 0;static int verbose_print(int level, char *format, ...){ va_list ap; int retval; if (verbose >= level) { va_start(ap, format); retval = vprintf(format, ap); va_end(ap); return retval; } return 0;}int main(int argc, char *argv[]){ struct hash h; struct hash_iterator hi; int i; int *t, *t_arr; while ((i = getopt(argc, argv, "vi:")) != -1) { switch (i) { case 'v': ++verbose; break; case 'i': MAX_ITEMS = atoi(optarg); break; default: fprintf(stderr, "bad option\n"); exit(1); } } assert(t_arr = malloc(MAX_ITEMS * sizeof(int))); hash_init(&h, 100, NULL); verbose_print(1, "start\n"); for (i = 0; i < MAX_ITEMS; i++) { t = t_arr + i; *t = i; assert(hash_put(&h, i * i, t) == 0); } assert(MAX_ITEMS == hash_count(&h)); verbose_print(1, "\nhash get\n"); for (i = 0; i < MAX_ITEMS; i++) { t = hash_get(&h, i * i, NULL); assert(t); assert(*t == i); verbose_print(2, "%d ", *t); } verbose_print(1, "\niterator test\n"); hash_iter_set(&hi, &h); while ((t = hash_iter_get(&hi, NULL))) verbose_print(2, "%d ", *t); hash_iter_end(&hi); verbose_print(1, "\nremove test\n"); for (i = 0; i < MAX_ITEMS; i += 3) { t = hash_remove(&h, i * i, NULL); assert(t); assert(*t == i); verbose_print(2, "%d ", *t); } verbose_print(1, "\nhash_put_check test\n"); for (i = 0; i < MAX_ITEMS; i += 3) { t = t_arr + i; assert(hash_put_check(&h, i * i, t, NULL) == 0); verbose_print(2, "%d ", *t); } for (i = 0; i < MAX_ITEMS; i += 3) { t = t_arr + i; assert(hash_put_check(&h, i * i, t, NULL) < 0); verbose_print(2, "%d ", *t); } for (i = 0; i < MAX_ITEMS; i += 3) { t = hash_remove(&h, i * i, NULL); assert(t); assert(*t == i); verbose_print(2, "%d ", *t); } verbose_print(1, "\nhash get\n"); for (i = 0; i < MAX_ITEMS; i++) { t = hash_get(&h, i * i, NULL); if ((i % 3) == 0) assert(t == NULL); else { assert(t); assert(*t == i); verbose_print(2, "%d ", *t); } } hash_free(&h); verbose_print(1, "\nOK\n"); return 0;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -