📄 hash.c
字号:
scan->next = NULL; } } } return next;}/* * Insert a node into the hash table. * Notes: * 1. It's illegal to insert more than the maximum number of nodes. The client * should verify that the hash table is not full before attempting an * insertion. * 2. The same key may not be inserted into a table twice. * 3. If the table is dynamic and the load factor is already at >= 2, * grow the table. * 4. We take the top N bits of the hash value to derive the chain index, * where N is the base 2 logarithm of the size of the hash table. */void hash_insert(hash_t *hash, hnode_t *node, void *key){ hash_val_t hkey, chain; assert (hash_val_t_bit != 0); assert (hash->count < hash->maxcount); /* 1 */ assert (hash_lookup(hash, key) == NULL); /* 2 */ if (hash->dynamic && hash->count >= hash->highmark) /* 3 */ grow_table(hash); hkey = hash->hash(key); chain = hkey & hash->mask; /* 4 */ node->key = key; node->hkey = hkey; node->pself = hash->table + chain; node->next = hash->table[chain]; if (node->next) node->next->pself = &node->next; hash->table[chain] = node; hash->count++; assert (hash_verify(hash));}/* * Find a node in the hash table and return a pointer to it. * Notes: * 1. We hash the key and keep the entire hash value. As an optimization, when * we descend down the chain, we can compare hash values first and only if * hash values match do we perform a full key comparison. * 2. To locate the chain from among 2^N chains, we look at the lower N bits of * the hash value by anding them with the current mask. * 3. Looping through the chain, we compare the stored hash value inside each * node against our computed hash. If they match, then we do a full * comparison between the unhashed keys. If these match, we have located the * entry. */hnode_t *hash_lookup(hash_t *hash, void *key){ hash_val_t hkey, chain; hnode_t *nptr; hkey = hash->hash(key); /* 1 */ chain = hkey & hash->mask; /* 2 */ for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */ if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0) return nptr; } return NULL;}/* * Delete the given node from the hash table. This is easy, because each node * contains a back pointer to the previous node's next pointer. * Notes: * 1. The node must belong to this hash table, and its key must not have * been tampered with. * 2. If there is a next node, then we must update its back pointer to * skip this node. * 3. We must update the pointer that is pointed at by the back-pointer * to skip over the node that is being deleted and instead point to * the successor (or to NULL if the node being deleted is the last one). */hnode_t *hash_delete(hash_t *hash, hnode_t *node){ assert (hash_lookup(hash, node->key) == node); /* 1 */ assert (hash_val_t_bit != 0); if (hash->dynamic && hash->count <= hash->lowmark && hash->count > INIT_SIZE) shrink_table(hash); if (node->next) /* 2 */ node->next->pself = node->pself; *node->pself = node->next; /* 3 */ hash->count--; assert (hash_verify(hash)); return node;}int hash_alloc_insert(hash_t *hash, void *key, void *data){ hnode_t *node = hash->allocnode(hash->context); if (node) { hnode_init(node, data); hash_insert(hash, node, key); return 1; } return 0;}void hash_delete_free(hash_t *hash, hnode_t *node){ hash_delete(hash, node); hash->freenode(node, hash->context);}/* * Exactly like hash_delete, except does not trigger table shrinkage. This is to be * used from within a hash table scan operation. See notes for hash_delete. */hnode_t *hash_scan_delete(hash_t *hash, hnode_t *node){ assert (hash_lookup(hash, node->key) == node); /* 1 */ assert (hash_val_t_bit != 0); if (node->next) /* 2 */ node->next->pself = node->pself; *node->pself = node->next; /* 3 */ hash->count--; assert (hash_verify(hash)); return node;}/* * Verify whether the given object is a valid hash table. This means * Notes: * 1. If the hash table is dynamic, verify whether the high and * low expansion/shrinkage thresholds are powers of two. * 2. For each chain, verify whether the back pointers are correctly * set. Count all nodes in the table, and test each hash value * to see whether it is correct for the node's chain. */int hash_verify(hash_t *hash){ hashcount_t count = 0; hash_val_t chain; hnode_t **npp; if (hash->dynamic) { /* 1 */ if (hash->lowmark >= hash->highmark) return 0; if (!is_power_of_two(hash->highmark)) return 0; if (!is_power_of_two(hash->lowmark)) return 0; } for (chain = 0; chain < hash->nchains; chain++) { /* 2 */ for (npp = hash->table + chain; *npp; npp = &(*npp)->next) { if ((*npp)->pself != npp) return 0; if (((*npp)->hkey & hash->mask) != chain) return 0; count++; } } if (count != hash->count) return 0; return 1;}/* * Test whether the hash table is full and return 1 if this is true, * 0 if it is false. */#undef hash_isfullint hash_isfull(hash_t *hash){ return hash->count == hash->maxcount;}/* * Test whether the hash table is empty and return 1 if this is true, * 0 if it is false. */#undef hash_isemptyint hash_isempty(hash_t *hash){ return hash->count == 0;}static hnode_t *hnode_alloc(void *context){ return malloc(sizeof *hnode_alloc(NULL));}static void hnode_free(hnode_t *node, void *context){ free(node);}/* * Create a hash table node dynamically and assign it the given data. */hnode_t *hnode_create(void *data){ hnode_t *node = malloc(sizeof *node); if (node) { node->data = data; node->next = NULL; node->pself = NULL; } return node;}/* * Initialize a client-supplied node */hnode_t *hnode_init(hnode_t *hnode, void *data){ hnode->data = data; hnode->next = NULL; hnode->pself = NULL; return hnode;}/* * Destroy a dynamically allocated node. */void hnode_destroy(hnode_t *hnode){ free(hnode);}#undef hnode_putvoid hnode_put(hnode_t *node, void *data){ node->data = data;}#undef hnode_getvoid *hnode_get(hnode_t *node){ return node->data;}#undef hnode_getkeyvoid *hnode_getkey(hnode_t *node){ return node->key;}#undef hash_counthashcount_t hash_count(hash_t *hash){ return hash->count;}#undef hash_sizehashcount_t hash_size(hash_t *hash){ return hash->nchains;}static hash_val_t hash_fun_default(const void *key){ static unsigned long randbox[] = { 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, }; const unsigned char *str = key; hash_val_t acc = 0; while (*str) { acc ^= randbox[(*str + acc) & 0xf]; acc = (acc << 1) | (acc >> 31); acc &= 0xffffffffU; acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; acc = (acc << 2) | (acc >> 30); acc &= 0xffffffffU; } return acc;}static int hash_comp_default(const void *key1, const void *key2){ return strcmp(key1, key2);}#ifdef KAZLIB_TEST_MAIN#include <stdio.h>#include <string.h>#include <ctype.h>#include <stdarg.h>typedef char input_t[256];static int tokenize(char *string, ...){ char **tokptr; va_list arglist; int tokcount = 0; va_start(arglist, string); tokptr = va_arg(arglist, char **); while (tokptr) { while (*string && isspace(*string)) string++; if (!*string) break; *tokptr = string; while (*string && !isspace(*string)) string++; tokptr = va_arg(arglist, char **); tokcount++; if (!*string) break; *string++ = 0; } va_end(arglist); return tokcount;}static char *dupstring(char *str){ int sz = strlen(str) + 1; char *new = malloc(sz); if (new) memcpy(new, str, sz); return new;}static hnode_t *new_node(void *c){ static hnode_t few[5]; static int count; if (count < 5) return few + count++; return NULL;}static void del_node(hnode_t *n, void *c){}int main(void){ input_t in; hash_t *h = hash_create(HASHCOUNT_T_MAX, 0, 0); hnode_t *hn; hscan_t hs; char *tok1, *tok2, *key, *val; int prompt = 0; char *help = "a <key> <val> add value to hash table\n" "d <key> delete value from hash table\n" "l <key> lookup value in hash table\n" "n show size of hash table\n" "c show number of entries\n" "t dump whole hash table\n" "+ increase hash table (private func)\n" "- decrease hash table (private func)\n" "b print hash_t_bit value\n" "p turn prompt on\n" "s switch to non-functioning allocator\n" "q quit"; if (!h) puts("hash_create failed"); for (;;) { if (prompt) putchar('>'); fflush(stdout); if (!fgets(in, sizeof(input_t), stdin)) break; switch(in[0]) { case '?': puts(help); break; case 'b': printf("%d\n", hash_val_t_bit); break; case 'a': if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { puts("what?"); break; } key = dupstring(tok1); if (!key) { puts("dupstring failed"); break; } val = dupstring(tok2); if (!val) { puts("dupstring failed"); free(key); break; } if (!hash_alloc_insert(h, key, val)) { puts("hash_alloc_insert failed"); free(key); free(val); break; } break; case 'd': if (tokenize(in+1, &tok1, (char **) 0) != 1) { puts("what?"); break; } hn = hash_lookup(h, tok1); if (!hn) { puts("hash_lookup failed"); break; } key = hnode_get(hn); val = hnode_getkey(hn); hash_delete_free(h, hn); free(key); free(val); break; case 'l': if (tokenize(in+1, &tok1, (char **) 0) != 1) { puts("what?"); break; } hn = hash_lookup(h, tok1); if (!hn) { puts("hash_lookup failed"); break; } val = hnode_get(hn); puts(val); break; case 'n': printf("%lu\n", (unsigned long) hash_size(h)); break; case 'c': printf("%lu\n", (unsigned long) hash_count(h)); break; case 't': hash_scan_begin(&hs, h); while ((hn = hash_scan_next(&hs))) printf("%s\t%s\n", (char*) hnode_getkey(hn), (char*) hnode_get(hn)); break; case '+': grow_table(h); /* private function */ break; case '-': shrink_table(h); /* private function */ break; case 'q': exit(0); break; case '\0': break; case 'p': prompt = 1; break; case 's': hash_set_allocator(h, new_node, del_node, NULL); break; default: putchar('?'); putchar('\n'); break; } } return 0;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -