blob: ea67dfc76c2eb41838778a0400b4579684b07a00 [file] [log] [blame]
Jari Aaltoccc6cda1996-12-23 17:02:34 +00001/* hashlib.c -- functions to manage and access hash tables for bash. */
Jari Aalto726f6381996-08-26 18:22:31 +00002
Jari Aalto31859422009-01-12 13:36:28 +00003/* Copyright (C) 1987,1989,1991,1995,1998,2001,2003,2005,2006,2008,2009 Free Software Foundation, Inc.
Jari Aalto726f6381996-08-26 18:22:31 +00004
Jari Aalto31859422009-01-12 13:36:28 +00005 This file is part of GNU Bash, the Bourne Again SHell.
Jari Aalto726f6381996-08-26 18:22:31 +00006
Jari Aalto31859422009-01-12 13:36:28 +00007 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.
Jari Aalto726f6381996-08-26 18:22:31 +000011
Jari Aalto31859422009-01-12 13:36:28 +000012 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.
Jari Aalto726f6381996-08-26 18:22:31 +000016
Jari Aalto31859422009-01-12 13:36:28 +000017 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*/
Jari Aalto726f6381996-08-26 18:22:31 +000020
Jari Aaltobb706242000-03-17 21:46:59 +000021#include <config.h>
Jari Aalto726f6381996-08-26 18:22:31 +000022
Jari Aaltod166f041997-06-05 14:59:13 +000023#include "bashansi.h"
Jari Aalto726f6381996-08-26 18:22:31 +000024
Jari Aaltoccc6cda1996-12-23 17:02:34 +000025#if defined (HAVE_UNISTD_H)
Jari Aaltocce855b1998-04-17 19:52:44 +000026# ifdef _MINIX
27# include <sys/types.h>
28# endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +000029# include <unistd.h>
30#endif
31
Jari Aaltod166f041997-06-05 14:59:13 +000032#include <stdio.h>
33
Jari Aalto726f6381996-08-26 18:22:31 +000034#include "shell.h"
Jari Aaltoccc6cda1996-12-23 17:02:34 +000035#include "hashlib.h"
Jari Aalto726f6381996-08-26 18:22:31 +000036
Jari Aalto7117c2d2002-07-17 14:10:11 +000037/* 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))
Jari Aaltof73dda02001-11-13 17:56:06 +000040
Jari Aalto7117c2d2002-07-17 14:10:11 +000041static BUCKET_CONTENTS *copy_bucket_array __P((BUCKET_CONTENTS *, sh_string_func_t *));
Jari Aalto726f6381996-08-26 18:22:31 +000042
43/* Make a new hash table with BUCKETS number of buckets. Initialize
44 each slot in the table to NULL. */
45HASH_TABLE *
Jari Aalto7117c2d2002-07-17 14:10:11 +000046hash_create (buckets)
Jari Aalto726f6381996-08-26 18:22:31 +000047 int buckets;
48{
Jari Aaltoccc6cda1996-12-23 17:02:34 +000049 HASH_TABLE *new_table;
Jari Aalto7117c2d2002-07-17 14:10:11 +000050 register int i;
Jari Aalto726f6381996-08-26 18:22:31 +000051
Jari Aaltoccc6cda1996-12-23 17:02:34 +000052 new_table = (HASH_TABLE *)xmalloc (sizeof (HASH_TABLE));
Jari Aalto726f6381996-08-26 18:22:31 +000053 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;
Jari Aalto7117c2d2002-07-17 14:10:11 +000060
61 for (i = 0; i < buckets; i++)
62 new_table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
63
Jari Aalto726f6381996-08-26 18:22:31 +000064 return (new_table);
65}
66
Jari Aaltof73dda02001-11-13 17:56:06 +000067int
Jari Aalto7117c2d2002-07-17 14:10:11 +000068hash_size (table)
Jari Aaltof73dda02001-11-13 17:56:06 +000069 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))
Jari Aalto7117c2d2002-07-17 14:10:11 +000099 : NULL;
100 n->khash = e->khash;
Jari Aaltof73dda02001-11-13 17:56:06 +0000101 n->times_found = e->times_found;
102 n->next = (BUCKET_CONTENTS *)NULL;
103 }
104
105 return new_bucket;
106}
107
108HASH_TABLE *
Jari Aalto7117c2d2002-07-17 14:10:11 +0000109hash_copy (table, cpdata)
Jari Aaltof73dda02001-11-13 17:56:06 +0000110 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
Jari Aalto7117c2d2002-07-17 14:10:11 +0000119 new_table = hash_create (table->nbuckets);
Jari Aaltof73dda02001-11-13 17:56:06 +0000120
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
Jari Aalto7117c2d2002-07-17 14:10:11 +0000128/* 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;
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
Jari Aalto726f6381996-08-26 18:22:31 +0000150/* Return the location of the bucket which should contain the data
151 for STRING. TABLE is a pointer to a HASH_TABLE. */
152
Jari Aalto726f6381996-08-26 18:22:31 +0000153int
Jari Aalto7117c2d2002-07-17 14:10:11 +0000154hash_bucket (string, table)
Jari Aaltof73dda02001-11-13 17:56:06 +0000155 const char *string;
Jari Aalto726f6381996-08-26 18:22:31 +0000156 HASH_TABLE *table;
157{
Jari Aalto7117c2d2002-07-17 14:10:11 +0000158 unsigned int h;
Jari Aalto726f6381996-08-26 18:22:31 +0000159
Jari Aalto7117c2d2002-07-17 14:10:11 +0000160 return (HASH_BUCKET (string, table, h));
Jari Aalto726f6381996-08-26 18:22:31 +0000161}
162
Jari Aalto7117c2d2002-07-17 14:10:11 +0000163/* 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. */
Jari Aalto726f6381996-08-26 18:22:31 +0000165BUCKET_CONTENTS *
Jari Aalto7117c2d2002-07-17 14:10:11 +0000166hash_search (string, table, flags)
Jari Aaltof73dda02001-11-13 17:56:06 +0000167 const char *string;
Jari Aalto726f6381996-08-26 18:22:31 +0000168 HASH_TABLE *table;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000169 int flags;
Jari Aalto726f6381996-08-26 18:22:31 +0000170{
171 BUCKET_CONTENTS *list;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000172 int bucket;
173 unsigned int hv;
Jari Aalto726f6381996-08-26 18:22:31 +0000174
Jari Aalto7117c2d2002-07-17 14:10:11 +0000175 if (table == 0 || ((flags & HASH_CREATE) == 0 && HASH_ENTRIES (table) == 0))
Jari Aalto726f6381996-08-26 18:22:31 +0000176 return (BUCKET_CONTENTS *)NULL;
177
Jari Aalto7117c2d2002-07-17 14:10:11 +0000178 bucket = HASH_BUCKET (string, table, hv);
Jari Aalto726f6381996-08-26 18:22:31 +0000179
Chet Ramey00018032011-11-21 20:51:19 -0500180 for (list = table->bucket_array ? table->bucket_array[bucket] : 0; list; list = list->next)
Jari Aalto726f6381996-08-26 18:22:31 +0000181 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000182 if (hv == list->khash && STREQ (list->key, string))
Jari Aalto726f6381996-08-26 18:22:31 +0000183 {
184 list->times_found++;
185 return (list);
186 }
Jari Aalto726f6381996-08-26 18:22:31 +0000187 }
Jari Aalto7117c2d2002-07-17 14:10:11 +0000188
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
Jari Aalto726f6381996-08-26 18:22:31 +0000204 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. */
210BUCKET_CONTENTS *
Jari Aalto7117c2d2002-07-17 14:10:11 +0000211hash_remove (string, table, flags)
Jari Aaltof73dda02001-11-13 17:56:06 +0000212 const char *string;
Jari Aalto726f6381996-08-26 18:22:31 +0000213 HASH_TABLE *table;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000214 int flags;
Jari Aalto726f6381996-08-26 18:22:31 +0000215{
Jari Aalto7117c2d2002-07-17 14:10:11 +0000216 int bucket;
Jari Aalto726f6381996-08-26 18:22:31 +0000217 BUCKET_CONTENTS *prev, *temp;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000218 unsigned int hv;
Jari Aalto726f6381996-08-26 18:22:31 +0000219
Jari Aalto7117c2d2002-07-17 14:10:11 +0000220 if (table == 0 || HASH_ENTRIES (table) == 0)
Jari Aalto726f6381996-08-26 18:22:31 +0000221 return (BUCKET_CONTENTS *)NULL;
222
Jari Aalto7117c2d2002-07-17 14:10:11 +0000223 bucket = HASH_BUCKET (string, table, hv);
Jari Aalto726f6381996-08-26 18:22:31 +0000224 prev = (BUCKET_CONTENTS *)NULL;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000225 for (temp = table->bucket_array[bucket]; temp; temp = temp->next)
Jari Aalto726f6381996-08-26 18:22:31 +0000226 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000227 if (hv == temp->khash && STREQ (temp->key, string))
Jari Aalto726f6381996-08-26 18:22:31 +0000228 {
229 if (prev)
230 prev->next = temp->next;
231 else
Jari Aalto7117c2d2002-07-17 14:10:11 +0000232 table->bucket_array[bucket] = temp->next;
Jari Aalto726f6381996-08-26 18:22:31 +0000233
234 table->nentries--;
235 return (temp);
236 }
237 prev = temp;
Jari Aalto726f6381996-08-26 18:22:31 +0000238 }
239 return ((BUCKET_CONTENTS *) NULL);
240}
241
242/* Create an entry for STRING, in TABLE. If the entry already
Jari Aalto7117c2d2002-07-17 14:10:11 +0000243 exists, then return it (unless the HASH_NOSRCH flag is set). */
Jari Aalto726f6381996-08-26 18:22:31 +0000244BUCKET_CONTENTS *
Jari Aalto7117c2d2002-07-17 14:10:11 +0000245hash_insert (string, table, flags)
Jari Aalto726f6381996-08-26 18:22:31 +0000246 char *string;
247 HASH_TABLE *table;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000248 int flags;
Jari Aalto726f6381996-08-26 18:22:31 +0000249{
250 BUCKET_CONTENTS *item;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000251 int bucket;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000252 unsigned int hv;
Jari Aalto726f6381996-08-26 18:22:31 +0000253
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000254 if (table == 0)
Jari Aalto7117c2d2002-07-17 14:10:11 +0000255 table = hash_create (0);
Jari Aalto726f6381996-08-26 18:22:31 +0000256
Jari Aalto7117c2d2002-07-17 14:10:11 +0000257 item = (flags & HASH_NOSRCH) ? (BUCKET_CONTENTS *)NULL
258 : hash_search (string, table, 0);
259
260 if (item == 0)
Jari Aalto726f6381996-08-26 18:22:31 +0000261 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000262 bucket = HASH_BUCKET (string, table, hv);
Jari Aalto726f6381996-08-26 18:22:31 +0000263
Jari Aalto7117c2d2002-07-17 14:10:11 +0000264 item = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
265 item->next = table->bucket_array[bucket];
266 table->bucket_array[bucket] = item;
Jari Aalto726f6381996-08-26 18:22:31 +0000267
Jari Aalto7117c2d2002-07-17 14:10:11 +0000268 item->data = NULL;
Jari Aalto726f6381996-08-26 18:22:31 +0000269 item->key = string;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000270 item->khash = hv;
Jari Aalto726f6381996-08-26 18:22:31 +0000271 item->times_found = 0;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000272
273 table->nentries++;
Jari Aalto726f6381996-08-26 18:22:31 +0000274 }
275
276 return (item);
277}
278
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000279/* 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. */
282void
Jari Aalto7117c2d2002-07-17 14:10:11 +0000283hash_flush (table, free_data)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000284 HASH_TABLE *table;
Jari Aaltof73dda02001-11-13 17:56:06 +0000285 sh_free_func_t *free_data;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000286{
287 int i;
288 register BUCKET_CONTENTS *bucket, *item;
289
Jari Aalto7117c2d2002-07-17 14:10:11 +0000290 if (table == 0 || HASH_ENTRIES (table) == 0)
Jari Aaltod166f041997-06-05 14:59:13 +0000291 return;
292
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000293 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 }
Jari Aalto7117c2d2002-07-17 14:10:11 +0000311
312 table->nentries = 0;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000313}
314
Jari Aaltod166f041997-06-05 14:59:13 +0000315/* Free the hash table pointed to by TABLE. */
316void
Jari Aalto7117c2d2002-07-17 14:10:11 +0000317hash_dispose (table)
Jari Aaltod166f041997-06-05 14:59:13 +0000318 HASH_TABLE *table;
319{
320 free (table->bucket_array);
321 free (table);
322}
323
Jari Aaltocce855b1998-04-17 19:52:44 +0000324void
Jari Aalto7117c2d2002-07-17 14:10:11 +0000325hash_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)
344void
345hash_pstats (table, name)
Jari Aaltod166f041997-06-05 14:59:13 +0000346 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 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000361 bc = hash_items (slot, table);
Jari Aaltod166f041997-06-05 14:59:13 +0000362
363 fprintf (stderr, "\tslot %3d: ", slot);
364 for (bcount = 0; bc; bc = bc->next)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000365 bcount++;
Jari Aaltod166f041997-06-05 14:59:13 +0000366
367 fprintf (stderr, "%d\n", bcount);
368 }
369}
Jari Aaltocce855b1998-04-17 19:52:44 +0000370#endif
Jari Aaltod166f041997-06-05 14:59:13 +0000371
Jari Aalto726f6381996-08-26 18:22:31 +0000372#ifdef TEST_HASHING
373
Jari Aalto7117c2d2002-07-17 14:10:11 +0000374/* link with xmalloc.o and lib/malloc/libmalloc.a */
Jari Aalto726f6381996-08-26 18:22:31 +0000375#undef NULL
376#include <stdio.h>
377
Jari Aaltof73dda02001-11-13 17:56:06 +0000378#ifndef NULL
379#define NULL 0
380#endif
381
382HASH_TABLE *table, *ntable;
Jari Aalto726f6381996-08-26 18:22:31 +0000383
Jari Aalto7117c2d2002-07-17 14:10:11 +0000384int interrupt_immediately = 0;
385
386int
387signal_is_trapped (s)
388 int s;
Jari Aalto726f6381996-08-26 18:22:31 +0000389{
Jari Aalto7117c2d2002-07-17 14:10:11 +0000390 return (0);
391}
392
393void
394programming_error (const char *format, ...)
395{
396 abort();
397}
398
399void
400fatal_error (const char *format, ...)
401{
402 abort();
Jari Aalto726f6381996-08-26 18:22:31 +0000403}
404
405main ()
406{
407 char string[256];
408 int count = 0;
409 BUCKET_CONTENTS *tt;
410
Jari Aalto7117c2d2002-07-17 14:10:11 +0000411 table = hash_create (0);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000412
Jari Aalto726f6381996-08-26 18:22:31 +0000413 for (;;)
414 {
415 char *temp_string;
416 if (fgets (string, sizeof (string), stdin) == 0)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000417 break;
Jari Aalto726f6381996-08-26 18:22:31 +0000418 if (!*string)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000419 break;
Jari Aalto726f6381996-08-26 18:22:31 +0000420 temp_string = savestring (string);
Jari Aalto7117c2d2002-07-17 14:10:11 +0000421 tt = hash_insert (temp_string, table, 0);
Jari Aalto726f6381996-08-26 18:22:31 +0000422 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 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000432
Jari Aalto7117c2d2002-07-17 14:10:11 +0000433 hash_pstats (table, "hash test");
Jari Aaltof73dda02001-11-13 17:56:06 +0000434
Jari Aalto7117c2d2002-07-17 14:10:11 +0000435 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");
Jari Aaltof73dda02001-11-13 17:56:06 +0000438
Jari Aaltod166f041997-06-05 14:59:13 +0000439 exit (0);
Jari Aalto726f6381996-08-26 18:22:31 +0000440}
441
442#endif /* TEST_HASHING */