]> git.ipfire.org Git - thirdparty/bash.git/blame - hashlib.c
Bash-4.4 patch 18
[thirdparty/bash.git] / hashlib.c
CommitLineData
ccc6cda3 1/* hashlib.c -- functions to manage and access hash tables for bash. */
726f6388 2
3185942a 3/* Copyright (C) 1987,1989,1991,1995,1998,2001,2003,2005,2006,2008,2009 Free Software Foundation, Inc.
726f6388 4
3185942a 5 This file is part of GNU Bash, the Bourne Again SHell.
726f6388 6
3185942a
JA
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.
726f6388 11
3185942a
JA
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.
726f6388 16
3185942a
JA
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*/
726f6388 20
bb70624e 21#include <config.h>
726f6388 22
d166f048 23#include "bashansi.h"
726f6388 24
ccc6cda3 25#if defined (HAVE_UNISTD_H)
cce855bc
JA
26# ifdef _MINIX
27# include <sys/types.h>
28# endif
ccc6cda3
JA
29# include <unistd.h>
30#endif
726f6388 31
d166f048
JA
32#include <stdio.h>
33
ccc6cda3
JA
34#include "shell.h"
35#include "hashlib.h"
726f6388 36
7117c2d2
JA
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))
f73dda09 40
7117c2d2 41static BUCKET_CONTENTS *copy_bucket_array __P((BUCKET_CONTENTS *, sh_string_func_t *));
726f6388
JA
42
43/* Make a new hash table with BUCKETS number of buckets. Initialize
44 each slot in the table to NULL. */
45HASH_TABLE *
7117c2d2 46hash_create (buckets)
726f6388
JA
47 int buckets;
48{
ccc6cda3 49 HASH_TABLE *new_table;
7117c2d2 50 register int i;
726f6388 51
ccc6cda3 52 new_table = (HASH_TABLE *)xmalloc (sizeof (HASH_TABLE));
726f6388
JA
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;
7117c2d2
JA
60
61 for (i = 0; i < buckets; i++)
62 new_table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
63
726f6388
JA
64 return (new_table);
65}
66
f73dda09 67int
7117c2d2 68hash_size (table)
f73dda09
JA
69 HASH_TABLE *table;
70{
71 return (HASH_ENTRIES(table));
72}
73
74static BUCKET_CONTENTS *
75copy_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))
7117c2d2
JA
99 : NULL;
100 n->khash = e->khash;
f73dda09
JA
101 n->times_found = e->times_found;
102 n->next = (BUCKET_CONTENTS *)NULL;
103 }
104
105 return new_bucket;
106}
107
108HASH_TABLE *
7117c2d2 109hash_copy (table, cpdata)
f73dda09
JA
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
7117c2d2 119 new_table = hash_create (table->nbuckets);
f73dda09
JA
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
7117c2d2
JA
128/* The `khash' check below requires that strings that compare equally with
129 strcmp hash to the same value. */
130unsigned int
131hash_string (s)
132 const char *s;
133{
134 register unsigned int i;
726f6388 135
7117c2d2 136 /* This is the best string hash function I found.
b72432fd 137
7117c2d2
JA
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. */
726f6388
JA
152
153int
7117c2d2 154hash_bucket (string, table)
f73dda09 155 const char *string;
726f6388
JA
156 HASH_TABLE *table;
157{
7117c2d2 158 unsigned int h;
726f6388 159
7117c2d2 160 return (HASH_BUCKET (string, table, h));
726f6388
JA
161}
162
7117c2d2
JA
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. */
726f6388 165BUCKET_CONTENTS *
7117c2d2 166hash_search (string, table, flags)
f73dda09 167 const char *string;
726f6388 168 HASH_TABLE *table;
7117c2d2 169 int flags;
726f6388
JA
170{
171 BUCKET_CONTENTS *list;
7117c2d2
JA
172 int bucket;
173 unsigned int hv;
726f6388 174
7117c2d2 175 if (table == 0 || ((flags & HASH_CREATE) == 0 && HASH_ENTRIES (table) == 0))
726f6388
JA
176 return (BUCKET_CONTENTS *)NULL;
177
7117c2d2 178 bucket = HASH_BUCKET (string, table, hv);
726f6388 179
0001803f 180 for (list = table->bucket_array ? table->bucket_array[bucket] : 0; list; list = list->next)
726f6388 181 {
a0c0a00f 182 /* This is the comparison function */
7117c2d2 183 if (hv == list->khash && STREQ (list->key, string))
726f6388
JA
184 {
185 list->times_found++;
186 return (list);
187 }
726f6388 188 }
7117c2d2
JA
189
190 if (flags & HASH_CREATE)
191 {
192 list = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
193 list->next = table->bucket_array[bucket];
194 table->bucket_array[bucket] = list;
195
196 list->data = NULL;
197 list->key = (char *)string; /* XXX fix later */
198 list->khash = hv;
199 list->times_found = 0;
200
201 table->nentries++;
202 return (list);
203 }
204
726f6388
JA
205 return (BUCKET_CONTENTS *)NULL;
206}
207
208/* Remove the item specified by STRING from the hash table TABLE.
209 The item removed is returned, so you can free its contents. If
210 the item isn't in this table NULL is returned. */
211BUCKET_CONTENTS *
7117c2d2 212hash_remove (string, table, flags)
f73dda09 213 const char *string;
726f6388 214 HASH_TABLE *table;
7117c2d2 215 int flags;
726f6388 216{
7117c2d2 217 int bucket;
726f6388 218 BUCKET_CONTENTS *prev, *temp;
7117c2d2 219 unsigned int hv;
726f6388 220
7117c2d2 221 if (table == 0 || HASH_ENTRIES (table) == 0)
726f6388
JA
222 return (BUCKET_CONTENTS *)NULL;
223
7117c2d2 224 bucket = HASH_BUCKET (string, table, hv);
726f6388 225 prev = (BUCKET_CONTENTS *)NULL;
7117c2d2 226 for (temp = table->bucket_array[bucket]; temp; temp = temp->next)
726f6388 227 {
7117c2d2 228 if (hv == temp->khash && STREQ (temp->key, string))
726f6388
JA
229 {
230 if (prev)
231 prev->next = temp->next;
232 else
7117c2d2 233 table->bucket_array[bucket] = temp->next;
726f6388
JA
234
235 table->nentries--;
236 return (temp);
237 }
238 prev = temp;
726f6388
JA
239 }
240 return ((BUCKET_CONTENTS *) NULL);
241}
242
243/* Create an entry for STRING, in TABLE. If the entry already
7117c2d2 244 exists, then return it (unless the HASH_NOSRCH flag is set). */
726f6388 245BUCKET_CONTENTS *
7117c2d2 246hash_insert (string, table, flags)
726f6388
JA
247 char *string;
248 HASH_TABLE *table;
7117c2d2 249 int flags;
726f6388
JA
250{
251 BUCKET_CONTENTS *item;
ccc6cda3 252 int bucket;
7117c2d2 253 unsigned int hv;
726f6388 254
ccc6cda3 255 if (table == 0)
7117c2d2 256 table = hash_create (0);
726f6388 257
7117c2d2
JA
258 item = (flags & HASH_NOSRCH) ? (BUCKET_CONTENTS *)NULL
259 : hash_search (string, table, 0);
260
261 if (item == 0)
726f6388 262 {
7117c2d2 263 bucket = HASH_BUCKET (string, table, hv);
726f6388 264
7117c2d2
JA
265 item = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
266 item->next = table->bucket_array[bucket];
267 table->bucket_array[bucket] = item;
726f6388 268
7117c2d2 269 item->data = NULL;
726f6388 270 item->key = string;
7117c2d2 271 item->khash = hv;
726f6388 272 item->times_found = 0;
7117c2d2
JA
273
274 table->nentries++;
726f6388
JA
275 }
276
277 return (item);
278}
279
ccc6cda3
JA
280/* Remove and discard all entries in TABLE. If FREE_DATA is non-null, it
281 is a function to call to dispose of a hash item's data. Otherwise,
282 free() is called. */
283void
7117c2d2 284hash_flush (table, free_data)
ccc6cda3 285 HASH_TABLE *table;
f73dda09 286 sh_free_func_t *free_data;
ccc6cda3
JA
287{
288 int i;
289 register BUCKET_CONTENTS *bucket, *item;
290
7117c2d2 291 if (table == 0 || HASH_ENTRIES (table) == 0)
d166f048
JA
292 return;
293
ccc6cda3
JA
294 for (i = 0; i < table->nbuckets; i++)
295 {
296 bucket = table->bucket_array[i];
297
298 while (bucket)
299 {
300 item = bucket;
301 bucket = bucket->next;
302
303 if (free_data)
304 (*free_data) (item->data);
305 else
306 free (item->data);
307 free (item->key);
308 free (item);
309 }
310 table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
311 }
7117c2d2
JA
312
313 table->nentries = 0;
ccc6cda3
JA
314}
315
d166f048
JA
316/* Free the hash table pointed to by TABLE. */
317void
7117c2d2 318hash_dispose (table)
d166f048
JA
319 HASH_TABLE *table;
320{
321 free (table->bucket_array);
322 free (table);
323}
324
7117c2d2
JA
325void
326hash_walk (table, func)
726f6388 327 HASH_TABLE *table;
7117c2d2 328 hash_wfunc *func;
726f6388 329{
7117c2d2
JA
330 register int i;
331 BUCKET_CONTENTS *item;
332
333 if (table == 0 || HASH_ENTRIES (table) == 0)
334 return;
335
336 for (i = 0; i < table->nbuckets; i++)
337 {
338 for (item = hash_items (i, table); item; item = item->next)
339 if ((*func) (item) < 0)
340 return;
341 }
726f6388
JA
342}
343
7117c2d2 344#if defined (DEBUG) || defined (TEST_HASHING)
cce855bc 345void
7117c2d2 346hash_pstats (table, name)
d166f048
JA
347 HASH_TABLE *table;
348 char *name;
349{
350 register int slot, bcount;
351 register BUCKET_CONTENTS *bc;
352
353 if (name == 0)
354 name = "unknown hash table";
355
356 fprintf (stderr, "%s: %d buckets; %d items\n", name, table->nbuckets, table->nentries);
357
358 /* Print out a count of how many strings hashed to each bucket, so we can
359 see how even the distribution is. */
360 for (slot = 0; slot < table->nbuckets; slot++)
361 {
7117c2d2 362 bc = hash_items (slot, table);
d166f048
JA
363
364 fprintf (stderr, "\tslot %3d: ", slot);
365 for (bcount = 0; bc; bc = bc->next)
28ef6c31 366 bcount++;
d166f048
JA
367
368 fprintf (stderr, "%d\n", bcount);
369 }
370}
cce855bc 371#endif
d166f048 372
726f6388
JA
373#ifdef TEST_HASHING
374
7117c2d2 375/* link with xmalloc.o and lib/malloc/libmalloc.a */
726f6388
JA
376#undef NULL
377#include <stdio.h>
378
f73dda09
JA
379#ifndef NULL
380#define NULL 0
381#endif
382
383HASH_TABLE *table, *ntable;
726f6388 384
7117c2d2
JA
385int interrupt_immediately = 0;
386
387int
388signal_is_trapped (s)
389 int s;
726f6388 390{
7117c2d2
JA
391 return (0);
392}
393
394void
395programming_error (const char *format, ...)
396{
397 abort();
398}
399
400void
401fatal_error (const char *format, ...)
402{
403 abort();
726f6388
JA
404}
405
a0c0a00f
CR
406void
407internal_warning (const char *format, ...)
408{
409}
410
726f6388
JA
411main ()
412{
413 char string[256];
414 int count = 0;
415 BUCKET_CONTENTS *tt;
416
a0c0a00f
CR
417#if defined (TEST_NBUCKETS)
418 table = hash_create (TEST_NBUCKETS);
419#else
7117c2d2 420 table = hash_create (0);
a0c0a00f 421#endif
ccc6cda3 422
726f6388
JA
423 for (;;)
424 {
425 char *temp_string;
426 if (fgets (string, sizeof (string), stdin) == 0)
28ef6c31 427 break;
726f6388 428 if (!*string)
28ef6c31 429 break;
726f6388 430 temp_string = savestring (string);
7117c2d2 431 tt = hash_insert (temp_string, table, 0);
726f6388
JA
432 if (tt->times_found)
433 {
434 fprintf (stderr, "You have already added item `%s'\n", string);
435 free (temp_string);
436 }
437 else
438 {
439 count++;
440 }
441 }
ccc6cda3 442
7117c2d2 443 hash_pstats (table, "hash test");
f73dda09 444
7117c2d2
JA
445 ntable = hash_copy (table, (sh_string_func_t *)NULL);
446 hash_flush (table, (sh_free_func_t *)NULL);
447 hash_pstats (ntable, "hash copy test");
f73dda09 448
d166f048 449 exit (0);
726f6388
JA
450}
451
452#endif /* TEST_HASHING */