1 #include <stdint.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 #define HASH_BUCKET_MAX (1024) 7 #define HASH_BUCKET_CAPACITY_MAX (256) 8 #define HASHTABLE_DEBUG 9 #define TRUE 1 10 #define FALSE 0 11 12 #ifdef HASHTABLE_DEBUG 13 #define DEBUG(format, ...) printf("[%s] [%d] : "format" ", __FUNCTION__, __LINE__, __VA_ARGS__) 14 #else 15 #define DEBUG(format, ...) 16 #endif 17 18 struct hash_bucket { 19 int capacity; /* 桶的容量 */ 20 void *hkey; /* hashtable的key */ 21 void *hdata; /* hashtable的data */ 22 struct hash_bucket *prev; 23 struct hash_bucket *next; 24 struct hash_bucket *tail; 25 }; 26 27 struct hash { 28 uint32_t (*hash_key)(void *); 29 int (*hash_cmp)(const void *, const void*, int len); 30 struct hash_bucket* bucket[HASH_BUCKET_MAX]; 31 }; 32 33 uint32_t string_hash_key(void* str); 34 int string_hash_cmp(const void* src, const void* dst); 35 36 static struct hash *g_htable = NULL; 37 38 void hash_create(); 39 void *hash_lookup(void *key); 40 int hash_add(void *key, void* data); 41 int hash_delete(void *key); 42 void hash_destroy(); 43 void hash_iter_print(); 44 45 #define bucket_free(bucket) 46 free(bucket->hkey); 47 free(bucket->hdata); 48 free(bucket); 49 bucket = NULL; 50 51 uint32_t string_hash_key(void* str) { 52 char *tmp = (char *)str; 53 uint32_t key = 0; 54 while(*tmp) 55 key = (key * 33) ^ (uint32_t)(tmp++); 56 57 DEBUG("string key : %u, index : %d", key, key%HASH_BUCKET_MAX); 58 59 return key%HASH_BUCKET_MAX; 60 } 61 62 int string_hash_cmp(const void* src, const void* dst, int len) { 63 if (!src || !dst) { 64 DEBUG("src addr: %p, dst addr: %p", src, dst); 65 return -1; 66 } 67 return strncmp((char *)src, (char *)dst, len); 68 } 69 70 void hash_create() { 71 if (g_htable) { 72 DEBUG("the default hashtable is already created"); 73 return; 74 } 75 76 g_htable = (struct hash *)malloc(sizeof(struct hash)); 77 if (!g_htable) { 78 DEBUG("memory alloc failed."); 79 return; 80 } 81 82 memset(g_htable, 0, sizeof(struct hash)); 83 84 g_htable->hash_key = string_hash_key; 85 g_htable->hash_cmp = string_hash_cmp; 86 87 return; 88 } 89 90 static void bucket_delete(struct hash_bucket** ptr) { 91 struct hash_bucket *bucket = *ptr; 92 struct hash_bucket *tmp; 93 94 while(bucket) { 95 tmp = bucket; 96 bucket = bucket->next; 97 bucket_free(tmp); 98 } 99 } 100 101 void hash_destroy() { 102 if (g_htable) { 103 for(int i=0; i<HASH_BUCKET_MAX; i++) { 104 if (g_htable->bucket[i]) { 105 bucket_delete(&g_htable->bucket[i]); 106 } 107 } 108 109 free(g_htable); 110 g_htable = NULL; 111 } 112 return; 113 } 114 115 #define lru_bucket_move(bucket, head) 116 bucket->next = head; 117 bucket->prev = NULL; 118 bucket->capacity = head->capacity; 119 120 head->prev = bucket; 121 head->tail = NULL; 122 123 void *hash_lookup(void *key) { 124 if (!key) { 125 DEBUG("input para is NULL "); 126 return NULL; 127 } 128 129 uint32_t index = g_htable->hash_key(key); 130 struct hash_bucket* head = g_htable->bucket[index]; 131 struct hash_bucket* bucket = head; 132 133 while(bucket) { 134 if (0 == g_htable->hash_cmp(key, bucket->hkey, strlen((char*)key))) { 135 if (head != bucket && bucket != head->tail) { 136 bucket->prev->next = bucket->next; 137 bucket->next->prev = bucket->prev; 138 bucket->tail = head->tail; 139 140 lru_bucket_move(bucket, head); 141 } else if (bucket == head->tail && head->capacity>1) { 142 bucket->prev->next = NULL; 143 bucket->tail = bucket->prev; 144 145 lru_bucket_move(bucket, head); 146 } 147 g_htable->bucket[index] = bucket; 148 return bucket->hdata; 149 } 150 bucket = bucket->next; 151 } 152 return NULL; 153 } 154 155 int hash_add(void *key, void* data) { 156 if (!key || !data) { 157 DEBUG("input para is NULL "); 158 return FALSE; 159 } 160 161 uint32_t index = g_htable->hash_key(key); 162 struct hash_bucket* head = g_htable->bucket[index]; 163 164 if (!head) { 165 head = (struct hash_bucket*)malloc(sizeof(struct hash_bucket)); 166 if (!head) { 167 DEBUG("no memory for more hash_bucket "); 168 return FALSE; 169 } 170 171 memset(head, 0, sizeof(*head)); 172 head->capacity++; 173 174 head->hkey = strdup((char *)key); 175 head->hdata = strdup((char *)data); 176 head->tail = head; 177 g_htable->bucket[index] = head; 178 return TRUE; 179 } 180 181 int capacity = head->capacity; 182 struct hash_bucket *new_bucket = 183 (struct hash_bucket *)malloc(sizeof(struct hash_bucket)); 184 185 if (!new_bucket) { 186 DEBUG("no memory for more hash_bucket "); 187 return FALSE; 188 } 189 190 if (capacity >= HASH_BUCKET_CAPACITY_MAX) { 191 struct hash_bucket *tail = head->tail; 192 head->tail = tail->prev; 193 194 tail->prev->next = NULL; 195 bucket_free(tail); 196 } 197 198 head->prev = new_bucket; 199 new_bucket->next = head; 200 new_bucket->capacity = capacity + 1; 201 new_bucket->tail = head->tail; 202 head->tail = NULL; 203 204 head->hkey = strdup((char *)key); 205 head->hdata = strdup((char *)data); 206 207 g_htable->bucket[index] = new_bucket; 208 209 return TRUE; 210 } 211 212 int hash_delete(void *key) { 213 if (!key) { 214 DEBUG("input para is NULL "); 215 return FALSE; 216 } 217 218 uint32_t index = g_htable->hash_key(key); 219 struct hash_bucket* head = g_htable->bucket[index]; 220 struct hash_bucket* bkt = head; 221 222 while(bkt) { 223 if (0 == g_htable->hash_cmp(key, bkt->hkey, strlen((char*)key))) { 224 if (head != bkt && bkt != head->tail) { 225 bkt->prev->next = bkt->next; 226 bkt->next->prev = bkt->prev; 227 228 } else if (bkt == head->tail && head->capacity>1) { 229 bkt->prev->next = NULL; 230 bkt->tail = bkt->prev; 231 232 } else { 233 if (bkt->next) { 234 bkt->next->tail = bkt->tail; 235 bkt->next->capacity = bkt->capacity; 236 bkt->next->prev = NULL; 237 g_htable->bucket[index] = bkt->next; 238 } else { 239 g_htable->bucket[index] = NULL; 240 } 241 } 242 243 bucket_free(bkt); 244 if (g_htable->bucket[index]) { 245 g_htable->bucket[index]->capacity--; 246 } 247 248 return TRUE; 249 } 250 bkt = bkt->next; 251 } 252 return FALSE; 253 } 254 255 static void bucket_print(struct hash_bucket** ptr) { 256 struct hash_bucket *bkt = *ptr; 257 struct hash_bucket *tmp; 258 259 while(bkt) { 260 printf("key=[%s],data=[%s] ", (char*)bkt->hkey, (char*)bkt->hdata); 261 bkt = bkt->next; 262 } 263 } 264 265 void hash_iter_print() { 266 if (g_htable) { 267 for(int i=0; i<HASH_BUCKET_MAX; i++) { 268 if (g_htable->bucket[i]) { 269 bucket_print(&g_htable->bucket[i]); 270 } 271 } 272 } 273 } 274 275 int main(int argc, char* argv[]) { 276 hash_create(); 277 278 hash_add("first", "danxi"); 279 hash_add("second", "test"); 280 hash_add("three", "sad code"); 281 hash_add("four", "let's go"); 282 283 hash_iter_print(); 284 285 char * t1 = (char *)hash_lookup("first"); 286 char * t2 = (char *)hash_lookup("second"); 287 288 printf("%s %s ", t1, t2); 289 printf("%s ", (char*)hash_lookup("four")); 290 291 hash_delete("four"); 292 hash_iter_print(); 293 hash_destroy(); 294 295 return 0; 296 }