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