summaryrefslogtreecommitdiff
path: root/hashlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'hashlib.c')
-rw-r--r--hashlib.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/hashlib.c b/hashlib.c
new file mode 100644
index 00000000..37e4dc9d
--- /dev/null
+++ b/hashlib.c
@@ -0,0 +1,308 @@
+/* hashlib.c -- functions to manage and access hash tables for bash. */
+
+/* Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GNU Bash, the Bourne Again SHell.
+
+Bash is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 1, or (at your option) any later
+version.
+
+Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with Bash; see the file COPYING. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h"
+
+#if defined (HAVE_STRING_H)
+# include <string.h>
+#else /* !HAVE_STRING_H */
+# include <strings.h>
+#endif /* !HAVE_STRING_H */
+
+#if defined (HAVE_STDLIB_H)
+# include <stdlib.h>
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "shell.h"
+#include "hashlib.h"
+
+/* Zero the buckets in TABLE. */
+static void
+initialize_hash_table (table)
+ HASH_TABLE *table;
+{
+ register int i;
+ for (i = 0; i < table->nbuckets; i++)
+ table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
+}
+
+/* Make a new hash table with BUCKETS number of buckets. Initialize
+ each slot in the table to NULL. */
+HASH_TABLE *
+make_hash_table (buckets)
+ int buckets;
+{
+ HASH_TABLE *new_table;
+
+ new_table = (HASH_TABLE *)xmalloc (sizeof (HASH_TABLE));
+ if (buckets == 0)
+ buckets = DEFAULT_HASH_BUCKETS;
+
+ new_table->bucket_array =
+ (BUCKET_CONTENTS **)xmalloc (buckets * sizeof (BUCKET_CONTENTS *));
+ new_table->nbuckets = buckets;
+ new_table->nentries = 0;
+ initialize_hash_table (new_table);
+ return (new_table);
+}
+
+/* Return the location of the bucket which should contain the data
+ for STRING. TABLE is a pointer to a HASH_TABLE. */
+
+#define ALL_ONES (~((unsigned long) 0))
+#define BITS(h, n) ((unsigned long)(h) & ~(ALL_ONES << (n)))
+
+int
+hash_string (string, table)
+ char *string;
+ HASH_TABLE *table;
+{
+ register unsigned int i = 0;
+
+ while (*string)
+ i = (i << 2) + *string++;
+
+ return (BITS (i, 31) % table->nbuckets);
+}
+
+/* Return a pointer to the hashed item, or NULL if the item
+ can't be found. */
+BUCKET_CONTENTS *
+find_hash_item (string, table)
+ char *string;
+ HASH_TABLE *table;
+{
+ BUCKET_CONTENTS *list;
+ int which_bucket;
+
+ if (table == 0)
+ return (BUCKET_CONTENTS *)NULL;
+
+ which_bucket = hash_string (string, table);
+
+ for (list = table->bucket_array[which_bucket]; list; list = list->next)
+ {
+ if (STREQ (list->key, string))
+ {
+ list->times_found++;
+ return (list);
+ }
+ }
+ return (BUCKET_CONTENTS *)NULL;
+}
+
+/* Remove the item specified by STRING from the hash table TABLE.
+ The item removed is returned, so you can free its contents. If
+ the item isn't in this table NULL is returned. */
+BUCKET_CONTENTS *
+remove_hash_item (string, table)
+ char *string;
+ HASH_TABLE *table;
+{
+ int the_bucket;
+ BUCKET_CONTENTS *prev, *temp;
+
+ if (table == 0)
+ return (BUCKET_CONTENTS *)NULL;
+
+ the_bucket = hash_string (string, table);
+ prev = (BUCKET_CONTENTS *)NULL;
+ for (temp = table->bucket_array[the_bucket]; temp; temp = temp->next)
+ {
+ if (STREQ (temp->key, string))
+ {
+ if (prev)
+ prev->next = temp->next;
+ else
+ table->bucket_array[the_bucket] = temp->next;
+
+ table->nentries--;
+ return (temp);
+ }
+ prev = temp;
+ }
+ return ((BUCKET_CONTENTS *) NULL);
+}
+
+/* Create an entry for STRING, in TABLE. If the entry already
+ exists, then return it. */
+BUCKET_CONTENTS *
+add_hash_item (string, table)
+ char *string;
+ HASH_TABLE *table;
+{
+ BUCKET_CONTENTS *item;
+ int bucket;
+
+ if (table == 0)
+ table = make_hash_table (0);
+
+ if ((item = find_hash_item (string, table)) == 0)
+ {
+ bucket = hash_string (string, table);
+ item = table->bucket_array[bucket];
+
+ while (item && item->next)
+ item = item->next;
+
+ if (item)
+ {
+ item->next = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
+ item = item->next;
+ }
+ else
+ {
+ table->bucket_array[bucket] =
+ (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
+ item = table->bucket_array[bucket];
+ }
+
+ item->data = (char *)NULL;
+ item->next = (BUCKET_CONTENTS *)NULL;
+ item->key = string;
+ table->nentries++;
+ item->times_found = 0;
+ }
+
+ return (item);
+}
+
+/* Remove and discard all entries in TABLE. If FREE_DATA is non-null, it
+ is a function to call to dispose of a hash item's data. Otherwise,
+ free() is called. */
+void
+flush_hash_table (table, free_data)
+ HASH_TABLE *table;
+ VFunction *free_data;
+{
+ int i;
+ register BUCKET_CONTENTS *bucket, *item;
+
+ for (i = 0; i < table->nbuckets; i++)
+ {
+ bucket = table->bucket_array[i];
+
+ while (bucket)
+ {
+ item = bucket;
+ bucket = bucket->next;
+
+ if (free_data)
+ (*free_data) (item->data);
+ else
+ free (item->data);
+ free (item->key);
+ free (item);
+ }
+ table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
+ }
+}
+
+/* Return the bucket_contents list of bucket BUCKET in TABLE. If
+ TABLE doesn't have BUCKET buckets, return NULL. */
+#undef get_hash_bucket
+BUCKET_CONTENTS *
+get_hash_bucket (bucket, table)
+ int bucket;
+ HASH_TABLE *table;
+{
+ if (table && bucket < table->nbuckets)
+ return (table->bucket_array[bucket]);
+ else
+ return (BUCKET_CONTENTS *)NULL;
+}
+
+#ifdef TEST_HASHING
+
+#undef NULL
+#include <stdio.h>
+
+HASH_TABLE *table;
+#define NBUCKETS 107
+
+char *
+xmalloc (bytes)
+ int bytes;
+{
+ char *result = (char *)malloc (bytes);
+ if (!result)
+ {
+ fprintf (stderr, "hash: out of virtual memory\n");
+ abort ();
+ }
+ return (result);
+}
+
+main ()
+{
+ char string[256];
+ int count = 0;
+ BUCKET_CONTENTS *tt;
+
+ table = make_hash_table (NBUCKETS);
+
+ for (;;)
+ {
+ char *temp_string;
+ if (fgets (string, sizeof (string), stdin) == 0)
+ break;
+ if (!*string)
+ break;
+ temp_string = savestring (string);
+ tt = add_hash_item (temp_string, table);
+ if (tt->times_found)
+ {
+ fprintf (stderr, "You have already added item `%s'\n", string);
+ free (temp_string);
+ }
+ else
+ {
+ count++;
+ }
+ }
+
+ printf ("You have entered %d (%d) items. The distribution is:\n",
+ table->nentries, count);
+
+ /* Print out a count of how many strings hashed to each bucket, so we can
+ see how even the distribution is. */
+ for (count = 0; count < table->nbuckets; count++)
+ {
+ int bcount;
+ register BUCKET_CONTENTS *list = get_hash_bucket (count, table);
+
+ printf ("slot %3d: ", count);
+ bcount = 0;
+
+ for (bcount = 0; list; list = list->next)
+ bcount++;
+
+ printf ("%d\n", bcount);
+ }
+ exit (0);
+}
+
+#endif /* TEST_HASHING */