]> git.ipfire.org Git - thirdparty/bash.git/blob - hashlib.c
Bash-4.3 patch 32
[thirdparty/bash.git] / hashlib.c
1 /* hashlib.c -- functions to manage and access hash tables for bash. */
2
3 /* Copyright (C) 1987,1989,1991,1995,1998,2001,2003,2005,2006,2008,2009 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
7 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 Bash is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #include "bashansi.h"
24
25 #if defined (HAVE_UNISTD_H)
26 # ifdef _MINIX
27 # include <sys/types.h>
28 # endif
29 # include <unistd.h>
30 #endif
31
32 #include <stdio.h>
33
34 #include "shell.h"
35 #include "hashlib.h"
36
37 /* Rely on properties of unsigned division (unsigned/int -> unsigned) and
38 don't discard the upper 32 bits of the value, if present. */
39 #define HASH_BUCKET(s, t, h) (((h) = hash_string (s)) & ((t)->nbuckets - 1))
40
41 static BUCKET_CONTENTS *copy_bucket_array __P((BUCKET_CONTENTS *, sh_string_func_t *));
42
43 /* Make a new hash table with BUCKETS number of buckets. Initialize
44 each slot in the table to NULL. */
45 HASH_TABLE *
46 hash_create (buckets)
47 int buckets;
48 {
49 HASH_TABLE *new_table;
50 register int i;
51
52 new_table = (HASH_TABLE *)xmalloc (sizeof (HASH_TABLE));
53 if (buckets == 0)
54 buckets = DEFAULT_HASH_BUCKETS;
55
56 new_table->bucket_array =
57 (BUCKET_CONTENTS **)xmalloc (buckets * sizeof (BUCKET_CONTENTS *));
58 new_table->nbuckets = buckets;
59 new_table->nentries = 0;
60
61 for (i = 0; i < buckets; i++)
62 new_table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
63
64 return (new_table);
65 }
66
67 int
68 hash_size (table)
69 HASH_TABLE *table;
70 {
71 return (HASH_ENTRIES(table));
72 }
73
74 static BUCKET_CONTENTS *
75 copy_bucket_array (ba, cpdata)
76 BUCKET_CONTENTS *ba;
77 sh_string_func_t *cpdata; /* data copy function */
78 {
79 BUCKET_CONTENTS *new_bucket, *n, *e;
80
81 if (ba == 0)
82 return ((BUCKET_CONTENTS *)0);
83
84 for (n = (BUCKET_CONTENTS *)0, e = ba; e; e = e->next)
85 {
86 if (n == 0)
87 {
88 new_bucket = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
89 n = new_bucket;
90 }
91 else
92 {
93 n->next = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
94 n = n->next;
95 }
96
97 n->key = savestring (e->key);
98 n->data = e->data ? (cpdata ? (*cpdata) (e->data) : savestring (e->data))
99 : NULL;
100 n->khash = e->khash;
101 n->times_found = e->times_found;
102 n->next = (BUCKET_CONTENTS *)NULL;
103 }
104
105 return new_bucket;
106 }
107
108 HASH_TABLE *
109 hash_copy (table, cpdata)
110 HASH_TABLE *table;
111 sh_string_func_t *cpdata;
112 {
113 HASH_TABLE *new_table;
114 int i;
115
116 if (table == 0)
117 return ((HASH_TABLE *)NULL);
118
119 new_table = hash_create (table->nbuckets);
120
121 for (i = 0; i < table->nbuckets; i++)
122 new_table->bucket_array[i] = copy_bucket_array (table->bucket_array[i], cpdata);
123
124 new_table->nentries = table->nentries;
125 return new_table;
126 }
127
128 /* The `khash' check below requires that strings that compare equally with
129 strcmp hash to the same value. */
130 unsigned int
131 hash_string (s)
132 const char *s;
133 {
134 register unsigned int i;
135
136 /* This is the best string hash function I found.
137
138 The magic is in the interesting relationship between the special prime
139 16777619 (2^24 + 403) and 2^32 and 2^8. */
140
141 for (i = 0; *s; s++)
142 {
143 i *= 16777619;
144 i ^= *s;
145 }
146
147 return i;
148 }
149
150 /* Return the location of the bucket which should contain the data
151 for STRING. TABLE is a pointer to a HASH_TABLE. */
152
153 int
154 hash_bucket (string, table)
155 const char *string;
156 HASH_TABLE *table;
157 {
158 unsigned int h;
159
160 return (HASH_BUCKET (string, table, h));
161 }
162
163 /* Return a pointer to the hashed item. If the HASH_CREATE flag is passed,
164 create a new hash table entry for STRING, otherwise return NULL. */
165 BUCKET_CONTENTS *
166 hash_search (string, table, flags)
167 const char *string;
168 HASH_TABLE *table;
169 int flags;
170 {
171 BUCKET_CONTENTS *list;
172 int bucket;
173 unsigned int hv;
174
175 if (table == 0 || ((flags & HASH_CREATE) == 0 && HASH_ENTRIES (table) == 0))
176 return (BUCKET_CONTENTS *)NULL;
177
178 bucket = HASH_BUCKET (string, table, hv);
179
180 for (list = table->bucket_array ? table->bucket_array[bucket] : 0; list; list = list->next)
181 {
182 if (hv == list->khash && STREQ (list->key, string))
183 {
184 list->times_found++;
185 return (list);
186 }
187 }
188
189 if (flags & HASH_CREATE)
190 {
191 list = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
192 list->next = table->bucket_array[bucket];
193 table->bucket_array[bucket] = list;
194
195 list->data = NULL;
196 list->key = (char *)string; /* XXX fix later */
197 list->khash = hv;
198 list->times_found = 0;
199
200 table->nentries++;
201 return (list);
202 }
203
204 return (BUCKET_CONTENTS *)NULL;
205 }
206
207 /* Remove the item specified by STRING from the hash table TABLE.
208 The item removed is returned, so you can free its contents. If
209 the item isn't in this table NULL is returned. */
210 BUCKET_CONTENTS *
211 hash_remove (string, table, flags)
212 const char *string;
213 HASH_TABLE *table;
214 int flags;
215 {
216 int bucket;
217 BUCKET_CONTENTS *prev, *temp;
218 unsigned int hv;
219
220 if (table == 0 || HASH_ENTRIES (table) == 0)
221 return (BUCKET_CONTENTS *)NULL;
222
223 bucket = HASH_BUCKET (string, table, hv);
224 prev = (BUCKET_CONTENTS *)NULL;
225 for (temp = table->bucket_array[bucket]; temp; temp = temp->next)
226 {
227 if (hv == temp->khash && STREQ (temp->key, string))
228 {
229 if (prev)
230 prev->next = temp->next;
231 else
232 table->bucket_array[bucket] = temp->next;
233
234 table->nentries--;
235 return (temp);
236 }
237 prev = temp;
238 }
239 return ((BUCKET_CONTENTS *) NULL);
240 }
241
242 /* Create an entry for STRING, in TABLE. If the entry already
243 exists, then return it (unless the HASH_NOSRCH flag is set). */
244 BUCKET_CONTENTS *
245 hash_insert (string, table, flags)
246 char *string;
247 HASH_TABLE *table;
248 int flags;
249 {
250 BUCKET_CONTENTS *item;
251 int bucket;
252 unsigned int hv;
253
254 if (table == 0)
255 table = hash_create (0);
256
257 item = (flags & HASH_NOSRCH) ? (BUCKET_CONTENTS *)NULL
258 : hash_search (string, table, 0);
259
260 if (item == 0)
261 {
262 bucket = HASH_BUCKET (string, table, hv);
263
264 item = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
265 item->next = table->bucket_array[bucket];
266 table->bucket_array[bucket] = item;
267
268 item->data = NULL;
269 item->key = string;
270 item->khash = hv;
271 item->times_found = 0;
272
273 table->nentries++;
274 }
275
276 return (item);
277 }
278
279 /* Remove and discard all entries in TABLE. If FREE_DATA is non-null, it
280 is a function to call to dispose of a hash item's data. Otherwise,
281 free() is called. */
282 void
283 hash_flush (table, free_data)
284 HASH_TABLE *table;
285 sh_free_func_t *free_data;
286 {
287 int i;
288 register BUCKET_CONTENTS *bucket, *item;
289
290 if (table == 0 || HASH_ENTRIES (table) == 0)
291 return;
292
293 for (i = 0; i < table->nbuckets; i++)
294 {
295 bucket = table->bucket_array[i];
296
297 while (bucket)
298 {
299 item = bucket;
300 bucket = bucket->next;
301
302 if (free_data)
303 (*free_data) (item->data);
304 else
305 free (item->data);
306 free (item->key);
307 free (item);
308 }
309 table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
310 }
311
312 table->nentries = 0;
313 }
314
315 /* Free the hash table pointed to by TABLE. */
316 void
317 hash_dispose (table)
318 HASH_TABLE *table;
319 {
320 free (table->bucket_array);
321 free (table);
322 }
323
324 void
325 hash_walk (table, func)
326 HASH_TABLE *table;
327 hash_wfunc *func;
328 {
329 register int i;
330 BUCKET_CONTENTS *item;
331
332 if (table == 0 || HASH_ENTRIES (table) == 0)
333 return;
334
335 for (i = 0; i < table->nbuckets; i++)
336 {
337 for (item = hash_items (i, table); item; item = item->next)
338 if ((*func) (item) < 0)
339 return;
340 }
341 }
342
343 #if defined (DEBUG) || defined (TEST_HASHING)
344 void
345 hash_pstats (table, name)
346 HASH_TABLE *table;
347 char *name;
348 {
349 register int slot, bcount;
350 register BUCKET_CONTENTS *bc;
351
352 if (name == 0)
353 name = "unknown hash table";
354
355 fprintf (stderr, "%s: %d buckets; %d items\n", name, table->nbuckets, table->nentries);
356
357 /* Print out a count of how many strings hashed to each bucket, so we can
358 see how even the distribution is. */
359 for (slot = 0; slot < table->nbuckets; slot++)
360 {
361 bc = hash_items (slot, table);
362
363 fprintf (stderr, "\tslot %3d: ", slot);
364 for (bcount = 0; bc; bc = bc->next)
365 bcount++;
366
367 fprintf (stderr, "%d\n", bcount);
368 }
369 }
370 #endif
371
372 #ifdef TEST_HASHING
373
374 /* link with xmalloc.o and lib/malloc/libmalloc.a */
375 #undef NULL
376 #include <stdio.h>
377
378 #ifndef NULL
379 #define NULL 0
380 #endif
381
382 HASH_TABLE *table, *ntable;
383
384 int interrupt_immediately = 0;
385
386 int
387 signal_is_trapped (s)
388 int s;
389 {
390 return (0);
391 }
392
393 void
394 programming_error (const char *format, ...)
395 {
396 abort();
397 }
398
399 void
400 fatal_error (const char *format, ...)
401 {
402 abort();
403 }
404
405 main ()
406 {
407 char string[256];
408 int count = 0;
409 BUCKET_CONTENTS *tt;
410
411 table = hash_create (0);
412
413 for (;;)
414 {
415 char *temp_string;
416 if (fgets (string, sizeof (string), stdin) == 0)
417 break;
418 if (!*string)
419 break;
420 temp_string = savestring (string);
421 tt = hash_insert (temp_string, table, 0);
422 if (tt->times_found)
423 {
424 fprintf (stderr, "You have already added item `%s'\n", string);
425 free (temp_string);
426 }
427 else
428 {
429 count++;
430 }
431 }
432
433 hash_pstats (table, "hash test");
434
435 ntable = hash_copy (table, (sh_string_func_t *)NULL);
436 hash_flush (table, (sh_free_func_t *)NULL);
437 hash_pstats (ntable, "hash copy test");
438
439 exit (0);
440 }
441
442 #endif /* TEST_HASHING */