1 /*
2 hashtbl - a handy and fast, free hash table implementation
3 Copyright (C) 2006 Ed L. Cashin
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20
21 /* hashtbl keys are always null-terminated arrays of char
22 *
23 * data are void pointers. Although hashtbl doesn't "own" the data
24 * stored in a hashtbl, the "hashtbl_free" function is provided as
25 * a convenience to the user: it frees the nodes in the hashtbl,
26 * calling free on each void pointer datum.
27 */
28 #include <config.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 /* support platforms that don't yet conform to C99 */
34 #if HAVE_STDINT_H
35 #include <stdint.h>
36 #elif HAVE_INTTYPES_H
37 #include <inttypes.h>
38 #else
39 #error No stdint.h or inttypes.h found.
40 #endif
41 #include "hashtbl.h"
42 #include "hgrow.h"
43 #include "hhash.h"
44 #ifdef ELC_FIND_LEAKS
45 #include "leakfind.h" /* checking for memory leaks */
46 #endif
47 /* #define DEBUG */
48
49 #ifndef HASHTBL_FULLISH
50 #define HASHTBL_FULLISH 0.60 /* ((entries / capacity) > this) means
51 * it's time to grow */
52 #endif
53
54 #ifndef HASHTBL_EMPTYISH
55 #define HASHTBL_EMPTYISH 0.05 /* ((entries / capacity) < this) means
56 * it's time to shrink */
57 #endif
58
59 #ifndef HASHTBL_BIG_CAPACITY
60 #define HASHTBL_BIG_CAPACITY 240101 /* if capacity bigger than this,
61 * we may occasionally shrink. */
62 #endif
63
64 #ifndef HASHTBL_DEFAULT_CAPACITY
65 #define HASHTBL_DEFAULT_CAPACITY 109
66 #endif
67
68 /* hashtbl strdup for n-char string */
h_strndup(const char * orig,size_t len)69 static char *h_strndup(const char *orig, size_t len)
70 {
71 char *newstr;
72
73 if ( (newstr = malloc(len + 1)) )
74 strcpy(newstr, orig);
75
76 return newstr;
77 }
78
hashtbl_init(hashtbl_t * h,size_t capacity)79 int hashtbl_init(hashtbl_t *h, size_t capacity)
80 {
81 int i;
82 hashnode_t **tbl;
83
84 if (! capacity)
85 capacity = HASHTBL_DEFAULT_CAPACITY;
86
87 h->capacity = capacity;
88 h->entries = 0;
89
90 if (! (tbl = malloc(sizeof(hashnode_t *) * capacity)) )
91 return -1; /* fatal_error("malloc table"); */
92 for (i = 0; i < capacity; ++i)
93 tbl[i] = NULL;
94 h->tbl = tbl;
95
96 return 0;
97 }
98
hashtbl_destroy(hashtbl_t * h)99 void hashtbl_destroy(hashtbl_t *h)
100 {
101 free(h->tbl);
102 }
103
hashtbl_find(hashtbl_t * h,const char * key,size_t keylen)104 inline static hashnode_t *hashtbl_find(hashtbl_t *h,
105 const char *key, size_t keylen)
106 {
107 hashnode_t *np;
108 hashnode_t **tbl = h->tbl;
109
110 for (np = tbl[hashtbl_cdb_hash(key, keylen) % h->capacity]; np; np = np->next)
111 if (! strcmp(key, np->key))
112 return np;
113
114 return NULL;
115 }
116
hashtbl_lookup(hashtbl_t * h,const char * key,size_t keylen)117 void *hashtbl_lookup(hashtbl_t *h, const char *key, size_t keylen)
118 {
119 hashnode_t *np;
120
121 if (! (np = hashtbl_find(h, key, keylen)) )
122 return NULL;
123 return np->data;
124 }
125
126 /* hashtbl doesn't do memory management of the stored data.
127 * When an entry is replaced by hashtbl_store, it returns the pointer
128 * to data that was replaced.
129 *
130 * The caller can use the returned pointer to free the associated
131 * memory appropriately.
132 */
hashtbl_store(hashtbl_t * h,const char * key,size_t keylen,void * data,void ** replaced)133 int hashtbl_store(hashtbl_t *h,
134 const char *key, size_t keylen,
135 void *data, void **replaced)
136 {
137 hashnode_t **tbl = h->tbl;
138 hashnode_t *np;
139 uint32_t hashval = hashtbl_cdb_hash(key, keylen) % h->capacity;
140
141 *replaced = NULL;
142
143 for (np = tbl[hashval]; np; np = np->next) {
144 if (! strcmp(key, np->key)) { /* replace entry with identical key */
145 *replaced = np->data;
146 np->data = data;
147 break;
148 }
149 }
150 if (! *replaced) {
151 if (! (np = malloc(sizeof(hashnode_t))) )
152 return -1; /* fatal_error("malloc hash table node"); */
153 if (! (np->key = h_strndup(key, keylen)))
154 return -1; /* fatal_error("duplicating hash key"); */
155 np->next = tbl[hashval];
156 tbl[hashval] = np;
157 np->data = data;
158 np->keylen = keylen;
159 ++h->entries;
160
161 if (((float) h->entries / h->capacity) > HASHTBL_FULLISH)
162 hashtbl_grow(h);
163 }
164
165 return 0;
166 }
167
hashtbl_node_destroy(hashnode_t * np)168 inline void hashtbl_node_destroy(hashnode_t *np)
169 {
170 /* hashtbl allocates memory for its nodes' keys */
171 free(np->key);
172 }
173
174 /* hashtbl doesn't do memory management of the stored data.
175 * When an entry is deleted by hashtbl_remove, it returns the pointer
176 * to data that was removed.
177 *
178 * The caller can use the returned pointer to free the associated
179 * memory appropriately.
180 */
hashtbl_remove(hashtbl_t * h,const char * key,size_t keylen)181 void *hashtbl_remove(hashtbl_t *h, const char *key, size_t keylen)
182 {
183 void *rmdata = NULL;
184 hashnode_t **tbl = h->tbl;
185 hashnode_t *np, *rmnode;
186 uint32_t hashval = hashtbl_cdb_hash(key, keylen) % h->capacity;
187
188 if (! (np = tbl[hashval]) )
189 return NULL;
190
191 /* test the entry in the table first; then go through the list
192 * if necessary */
193 if (! strcmp(np->key, key)) {
194 rmdata = np->data;
195 tbl[hashval] = np->next;
196 hashtbl_node_destroy(np);
197 free(np);
198 --h->entries;
199 } else {
200 for (; (rmnode = np->next); np = np->next) {
201 if (! strcmp(key, rmnode->key)) {
202 rmdata = rmnode->data;
203 np->next = rmnode->next;
204 hashtbl_node_destroy(rmnode);
205 free(rmnode);
206 --h->entries;
207 break;
208 }
209 }
210 }
211
212 if ((h->capacity > HASHTBL_BIG_CAPACITY)
213 && (((float) h->entries / h->capacity) < HASHTBL_EMPTYISH))
214 hashtbl_shrink(h);
215
216 return rmdata;
217 }
218