]> git.ipfire.org Git - thirdparty/bash.git/blame - hashlib.c
Bash-5.2 patch 26: fix typo when specifying readline's custom color prefix
[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
8868edaf
CR
37/* tunable constants for rehashing */
38#define HASH_REHASH_MULTIPLIER 4
39#define HASH_REHASH_FACTOR 2
40
41#define HASH_SHOULDGROW(table) \
42 ((table)->nentries >= (table)->nbuckets * HASH_REHASH_FACTOR)
43
44/* an initial approximation */
45#define HASH_SHOULDSHRINK(table) \
46 (((table)->nbuckets > DEFAULT_HASH_BUCKETS) && \
47 ((table)->nentries < (table)->nbuckets / HASH_REHASH_MULTIPLIER))
48
7117c2d2
JA
49/* Rely on properties of unsigned division (unsigned/int -> unsigned) and
50 don't discard the upper 32 bits of the value, if present. */
51#define HASH_BUCKET(s, t, h) (((h) = hash_string (s)) & ((t)->nbuckets - 1))
f73dda09 52
8868edaf
CR
53static BUCKET_CONTENTS *copy_bucket_array PARAMS((BUCKET_CONTENTS *, sh_string_func_t *));
54
55static void hash_rehash PARAMS((HASH_TABLE *, int));
56static void hash_grow PARAMS((HASH_TABLE *));
57static void hash_shrink PARAMS((HASH_TABLE *));
726f6388
JA
58
59/* Make a new hash table with BUCKETS number of buckets. Initialize
60 each slot in the table to NULL. */
61HASH_TABLE *
7117c2d2 62hash_create (buckets)
726f6388
JA
63 int buckets;
64{
ccc6cda3 65 HASH_TABLE *new_table;
7117c2d2 66 register int i;
726f6388 67
ccc6cda3 68 new_table = (HASH_TABLE *)xmalloc (sizeof (HASH_TABLE));
726f6388
JA
69 if (buckets == 0)
70 buckets = DEFAULT_HASH_BUCKETS;
71
72 new_table->bucket_array =
73 (BUCKET_CONTENTS **)xmalloc (buckets * sizeof (BUCKET_CONTENTS *));
74 new_table->nbuckets = buckets;
75 new_table->nentries = 0;
7117c2d2
JA
76
77 for (i = 0; i < buckets; i++)
78 new_table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
79
726f6388
JA
80 return (new_table);
81}
82
f73dda09 83int
7117c2d2 84hash_size (table)
f73dda09
JA
85 HASH_TABLE *table;
86{
87 return (HASH_ENTRIES(table));
88}
89
90static BUCKET_CONTENTS *
91copy_bucket_array (ba, cpdata)
92 BUCKET_CONTENTS *ba;
93 sh_string_func_t *cpdata; /* data copy function */
94{
95 BUCKET_CONTENTS *new_bucket, *n, *e;
96
97 if (ba == 0)
98 return ((BUCKET_CONTENTS *)0);
99
100 for (n = (BUCKET_CONTENTS *)0, e = ba; e; e = e->next)
101 {
102 if (n == 0)
103 {
104 new_bucket = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
105 n = new_bucket;
106 }
107 else
108 {
109 n->next = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
110 n = n->next;
111 }
112
113 n->key = savestring (e->key);
114 n->data = e->data ? (cpdata ? (*cpdata) (e->data) : savestring (e->data))
7117c2d2
JA
115 : NULL;
116 n->khash = e->khash;
f73dda09
JA
117 n->times_found = e->times_found;
118 n->next = (BUCKET_CONTENTS *)NULL;
119 }
120
121 return new_bucket;
122}
123
8868edaf
CR
124static void
125hash_rehash (table, nsize)
126 HASH_TABLE *table;
127 int nsize;
128{
129 int osize, i, j;
130 BUCKET_CONTENTS **old_bucket_array, *item, *next;
131
132 if (table == NULL || nsize == table->nbuckets)
133 return;
134
135 osize = table->nbuckets;
136 old_bucket_array = table->bucket_array;
137
138 table->nbuckets = nsize;
139 table->bucket_array = (BUCKET_CONTENTS **)xmalloc (table->nbuckets * sizeof (BUCKET_CONTENTS *));
140 for (i = 0; i < table->nbuckets; i++)
141 table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
142
143 for (j = 0; j < osize; j++)
144 {
145 for (item = old_bucket_array[j]; item; item = next)
146 {
147 next = item->next;
148 i = item->khash & (table->nbuckets - 1);
149 item->next = table->bucket_array[i];
150 table->bucket_array[i] = item;
151 }
152 }
153
154 free (old_bucket_array);
155}
156
157static void
158hash_grow (table)
159 HASH_TABLE *table;
160{
161 int nsize;
162
163 nsize = table->nbuckets * HASH_REHASH_MULTIPLIER;
164 if (nsize > 0) /* overflow */
165 hash_rehash (table, nsize);
166}
167
168static void
169hash_shrink (table)
170 HASH_TABLE *table;
171{
172 int nsize;
173
174 nsize = table->nbuckets / HASH_REHASH_MULTIPLIER;
175 hash_rehash (table, nsize);
176}
177
f73dda09 178HASH_TABLE *
7117c2d2 179hash_copy (table, cpdata)
f73dda09
JA
180 HASH_TABLE *table;
181 sh_string_func_t *cpdata;
182{
183 HASH_TABLE *new_table;
184 int i;
185
186 if (table == 0)
187 return ((HASH_TABLE *)NULL);
188
7117c2d2 189 new_table = hash_create (table->nbuckets);
f73dda09
JA
190
191 for (i = 0; i < table->nbuckets; i++)
192 new_table->bucket_array[i] = copy_bucket_array (table->bucket_array[i], cpdata);
193
194 new_table->nentries = table->nentries;
195 return new_table;
196}
197
d233b485
CR
198/* This is the best 32-bit string hash function I found. It's one of the
199 Fowler-Noll-Vo family (FNV-1).
200
201 The magic is in the interesting relationship between the special prime
202 16777619 (2^24 + 403) and 2^32 and 2^8. */
203
204#define FNV_OFFSET 2166136261
205#define FNV_PRIME 16777619
206
207/* If you want to use 64 bits, use
208FNV_OFFSET 14695981039346656037
8868edaf 209FNV_PRIME 1099511628211
d233b485
CR
210*/
211
7117c2d2
JA
212/* The `khash' check below requires that strings that compare equally with
213 strcmp hash to the same value. */
214unsigned int
215hash_string (s)
216 const char *s;
217{
218 register unsigned int i;
726f6388 219
d233b485 220 for (i = FNV_OFFSET; *s; s++)
7117c2d2 221 {
8868edaf
CR
222 /* FNV-1a has the XOR first, traditional FNV-1 has the multiply first */
223
224 /* was i *= FNV_PRIME */
225 i += (i<<1) + (i<<4) + (i<<7) + (i<<8) + (i<<24);
7117c2d2
JA
226 i ^= *s;
227 }
228
229 return i;
230}
231
232/* Return the location of the bucket which should contain the data
233 for STRING. TABLE is a pointer to a HASH_TABLE. */
726f6388
JA
234
235int
7117c2d2 236hash_bucket (string, table)
f73dda09 237 const char *string;
726f6388
JA
238 HASH_TABLE *table;
239{
7117c2d2 240 unsigned int h;
726f6388 241
7117c2d2 242 return (HASH_BUCKET (string, table, h));
726f6388
JA
243}
244
7117c2d2
JA
245/* Return a pointer to the hashed item. If the HASH_CREATE flag is passed,
246 create a new hash table entry for STRING, otherwise return NULL. */
726f6388 247BUCKET_CONTENTS *
7117c2d2 248hash_search (string, table, flags)
f73dda09 249 const char *string;
726f6388 250 HASH_TABLE *table;
7117c2d2 251 int flags;
726f6388
JA
252{
253 BUCKET_CONTENTS *list;
7117c2d2
JA
254 int bucket;
255 unsigned int hv;
726f6388 256
7117c2d2 257 if (table == 0 || ((flags & HASH_CREATE) == 0 && HASH_ENTRIES (table) == 0))
726f6388
JA
258 return (BUCKET_CONTENTS *)NULL;
259
7117c2d2 260 bucket = HASH_BUCKET (string, table, hv);
726f6388 261
0001803f 262 for (list = table->bucket_array ? table->bucket_array[bucket] : 0; list; list = list->next)
726f6388 263 {
a0c0a00f 264 /* This is the comparison function */
7117c2d2 265 if (hv == list->khash && STREQ (list->key, string))
726f6388
JA
266 {
267 list->times_found++;
268 return (list);
269 }
726f6388 270 }
7117c2d2
JA
271
272 if (flags & HASH_CREATE)
273 {
8868edaf
CR
274 if (HASH_SHOULDGROW (table))
275 {
276 hash_grow (table);
277 bucket = HASH_BUCKET (string, table, hv);
278 }
279
7117c2d2
JA
280 list = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
281 list->next = table->bucket_array[bucket];
282 table->bucket_array[bucket] = list;
283
284 list->data = NULL;
285 list->key = (char *)string; /* XXX fix later */
286 list->khash = hv;
287 list->times_found = 0;
288
289 table->nentries++;
290 return (list);
291 }
292
726f6388
JA
293 return (BUCKET_CONTENTS *)NULL;
294}
295
296/* Remove the item specified by STRING from the hash table TABLE.
297 The item removed is returned, so you can free its contents. If
298 the item isn't in this table NULL is returned. */
299BUCKET_CONTENTS *
7117c2d2 300hash_remove (string, table, flags)
f73dda09 301 const char *string;
726f6388 302 HASH_TABLE *table;
7117c2d2 303 int flags;
726f6388 304{
7117c2d2 305 int bucket;
726f6388 306 BUCKET_CONTENTS *prev, *temp;
7117c2d2 307 unsigned int hv;
726f6388 308
7117c2d2 309 if (table == 0 || HASH_ENTRIES (table) == 0)
726f6388
JA
310 return (BUCKET_CONTENTS *)NULL;
311
7117c2d2 312 bucket = HASH_BUCKET (string, table, hv);
726f6388 313 prev = (BUCKET_CONTENTS *)NULL;
7117c2d2 314 for (temp = table->bucket_array[bucket]; temp; temp = temp->next)
726f6388 315 {
7117c2d2 316 if (hv == temp->khash && STREQ (temp->key, string))
726f6388
JA
317 {
318 if (prev)
319 prev->next = temp->next;
320 else
7117c2d2 321 table->bucket_array[bucket] = temp->next;
726f6388
JA
322
323 table->nentries--;
324 return (temp);
325 }
326 prev = temp;
726f6388
JA
327 }
328 return ((BUCKET_CONTENTS *) NULL);
329}
330
331/* Create an entry for STRING, in TABLE. If the entry already
7117c2d2 332 exists, then return it (unless the HASH_NOSRCH flag is set). */
726f6388 333BUCKET_CONTENTS *
7117c2d2 334hash_insert (string, table, flags)
726f6388
JA
335 char *string;
336 HASH_TABLE *table;
7117c2d2 337 int flags;
726f6388
JA
338{
339 BUCKET_CONTENTS *item;
ccc6cda3 340 int bucket;
7117c2d2 341 unsigned int hv;
726f6388 342
ccc6cda3 343 if (table == 0)
7117c2d2 344 table = hash_create (0);
726f6388 345
7117c2d2
JA
346 item = (flags & HASH_NOSRCH) ? (BUCKET_CONTENTS *)NULL
347 : hash_search (string, table, 0);
348
349 if (item == 0)
726f6388 350 {
8868edaf
CR
351 if (HASH_SHOULDGROW (table))
352 hash_grow (table);
353
7117c2d2 354 bucket = HASH_BUCKET (string, table, hv);
726f6388 355
7117c2d2
JA
356 item = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
357 item->next = table->bucket_array[bucket];
358 table->bucket_array[bucket] = item;
726f6388 359
7117c2d2 360 item->data = NULL;
726f6388 361 item->key = string;
7117c2d2 362 item->khash = hv;
726f6388 363 item->times_found = 0;
7117c2d2
JA
364
365 table->nentries++;
726f6388
JA
366 }
367
368 return (item);
369}
370
ccc6cda3
JA
371/* Remove and discard all entries in TABLE. If FREE_DATA is non-null, it
372 is a function to call to dispose of a hash item's data. Otherwise,
373 free() is called. */
374void
7117c2d2 375hash_flush (table, free_data)
ccc6cda3 376 HASH_TABLE *table;
f73dda09 377 sh_free_func_t *free_data;
ccc6cda3
JA
378{
379 int i;
380 register BUCKET_CONTENTS *bucket, *item;
381
7117c2d2 382 if (table == 0 || HASH_ENTRIES (table) == 0)
d166f048
JA
383 return;
384
ccc6cda3
JA
385 for (i = 0; i < table->nbuckets; i++)
386 {
387 bucket = table->bucket_array[i];
388
389 while (bucket)
390 {
391 item = bucket;
392 bucket = bucket->next;
393
394 if (free_data)
395 (*free_data) (item->data);
396 else
397 free (item->data);
398 free (item->key);
399 free (item);
400 }
401 table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
402 }
7117c2d2
JA
403
404 table->nentries = 0;
ccc6cda3
JA
405}
406
d166f048
JA
407/* Free the hash table pointed to by TABLE. */
408void
7117c2d2 409hash_dispose (table)
d166f048
JA
410 HASH_TABLE *table;
411{
412 free (table->bucket_array);
413 free (table);
414}
415
7117c2d2
JA
416void
417hash_walk (table, func)
726f6388 418 HASH_TABLE *table;
7117c2d2 419 hash_wfunc *func;
726f6388 420{
7117c2d2
JA
421 register int i;
422 BUCKET_CONTENTS *item;
423
424 if (table == 0 || HASH_ENTRIES (table) == 0)
425 return;
426
427 for (i = 0; i < table->nbuckets; i++)
428 {
429 for (item = hash_items (i, table); item; item = item->next)
430 if ((*func) (item) < 0)
431 return;
432 }
726f6388
JA
433}
434
7117c2d2 435#if defined (DEBUG) || defined (TEST_HASHING)
cce855bc 436void
7117c2d2 437hash_pstats (table, name)
d166f048
JA
438 HASH_TABLE *table;
439 char *name;
440{
441 register int slot, bcount;
442 register BUCKET_CONTENTS *bc;
443
444 if (name == 0)
445 name = "unknown hash table";
446
447 fprintf (stderr, "%s: %d buckets; %d items\n", name, table->nbuckets, table->nentries);
448
449 /* Print out a count of how many strings hashed to each bucket, so we can
450 see how even the distribution is. */
451 for (slot = 0; slot < table->nbuckets; slot++)
452 {
7117c2d2 453 bc = hash_items (slot, table);
d166f048
JA
454
455 fprintf (stderr, "\tslot %3d: ", slot);
456 for (bcount = 0; bc; bc = bc->next)
28ef6c31 457 bcount++;
d166f048
JA
458
459 fprintf (stderr, "%d\n", bcount);
460 }
461}
cce855bc 462#endif
d166f048 463
726f6388
JA
464#ifdef TEST_HASHING
465
7117c2d2 466/* link with xmalloc.o and lib/malloc/libmalloc.a */
726f6388
JA
467#undef NULL
468#include <stdio.h>
469
f73dda09
JA
470#ifndef NULL
471#define NULL 0
472#endif
473
474HASH_TABLE *table, *ntable;
726f6388 475
7117c2d2 476int interrupt_immediately = 0;
8868edaf 477int running_trap = 0;
7117c2d2
JA
478
479int
480signal_is_trapped (s)
481 int s;
726f6388 482{
7117c2d2
JA
483 return (0);
484}
485
486void
487programming_error (const char *format, ...)
488{
489 abort();
490}
491
492void
493fatal_error (const char *format, ...)
494{
495 abort();
726f6388
JA
496}
497
a0c0a00f
CR
498void
499internal_warning (const char *format, ...)
500{
501}
502
8868edaf 503int
726f6388
JA
504main ()
505{
506 char string[256];
507 int count = 0;
508 BUCKET_CONTENTS *tt;
509
a0c0a00f
CR
510#if defined (TEST_NBUCKETS)
511 table = hash_create (TEST_NBUCKETS);
512#else
7117c2d2 513 table = hash_create (0);
a0c0a00f 514#endif
ccc6cda3 515
726f6388
JA
516 for (;;)
517 {
518 char *temp_string;
519 if (fgets (string, sizeof (string), stdin) == 0)
28ef6c31 520 break;
726f6388 521 if (!*string)
28ef6c31 522 break;
726f6388 523 temp_string = savestring (string);
7117c2d2 524 tt = hash_insert (temp_string, table, 0);
726f6388
JA
525 if (tt->times_found)
526 {
527 fprintf (stderr, "You have already added item `%s'\n", string);
528 free (temp_string);
529 }
530 else
531 {
532 count++;
533 }
534 }
ccc6cda3 535
7117c2d2 536 hash_pstats (table, "hash test");
f73dda09 537
7117c2d2
JA
538 ntable = hash_copy (table, (sh_string_func_t *)NULL);
539 hash_flush (table, (sh_free_func_t *)NULL);
540 hash_pstats (ntable, "hash copy test");
f73dda09 541
d166f048 542 exit (0);
726f6388
JA
543}
544
545#endif /* TEST_HASHING */