# hash.c: hash table operations. # # Copyright (C) 1994, 1995, 1996, 2011 Free Software Foundation, Inc. # # This program 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 3 of the License, or # (at your option) any later version. # # This program 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 this program. If not, see . # #include "config.h" #include "hash.h" #include "str-list.h" /* The hash function. We go for simplicity here. */ /* All our hash tables are related to filenames. */ #ifdef MONOCASE_FILENAMES #define TRANSFORM(x) toupper (x) #else #define TRANSFORM(x) (x) #endif static unsigned hash P2C(hash_table_type, table, const_string, key) { unsigned n = 0; /* Our keys aren't often anagrams of each other, so no point in weighting the characters. */ while (*key != 0) n = (n + n + TRANSFORM (*key++)) % table.size; return n; } hash_table_type hash_create P1C(unsigned, size) { unsigned b; hash_table_type ret; ret.buckets = XTALLOC (size, hash_element_type *); ret.size = size; /* calloc's zeroes aren't necessarily NULL, according to ANSI, so be safe. (Not that I know of any exceptions in reality.) */ for (b = 0; b key = key; new_elt->value = value; new_elt->next = NULL; /* Insert the new element at the end of the list. */ if (!table->buckets[n]) /* first element in bucket is a special case. */ table->buckets[n] = new_elt; else { hash_element_type *loc = table->buckets[n]; while (loc->next) /* Find the last element. */ loc = loc->next; loc->next = new_elt; /* Insert the new one after. */ } } /* Look up STR in MAP. Return a (dynamically-allocated) list of the corresponding strings or NULL if no match. */ #ifdef KPSE_DEBUG /* Print the hash values as integers if this is nonzero. */ boolean kpse_debug_hash_lookup_int = false; #endif string * hash_lookup P2C(hash_table_type, table, const_string, key) { hash_element_type *p; str_list_type ret; unsigned n = hash (table, key); ret = str_list_init (); /* Look at everything in this bucket. */ for (p = table.buckets[n]; p != NULL; p = p->next) if (FILESTRCASEEQ (key, p->key)) /* Cast because the general str_list_type shouldn't force const data. */ str_list_add (&ret, (string) p->value); /* If we found anything, mark end of list with null. */ if (STR_LIST (ret)) str_list_add (&ret, NULL); #ifdef KPSE_DEBUG if (KPSE_DEBUG_P (KPSE_DEBUG_HASH)) { DEBUGF1 ("hash_lookup(%s) =>", key); if (!STR_LIST (ret)) fputs (" (nil)\n", stderr); else { string *r; for (r = STR_LIST (ret); *r; r++) { putc (' ', stderr); if (kpse_debug_hash_lookup_int) fprintf (stderr, "%ld", (long) *r); else fputs (*r, stderr); } putc ('\n', stderr); } fflush (stderr); } #endif return STR_LIST (ret); } /* We only print nonempty buckets, to decrease output volume. */ void hash_print P2C(hash_table_type, table, boolean, summary_only) { unsigned b; unsigned total_elements = 0, total_buckets = 0; for (b = 0; b < table.size; b++) { hash_element_type *bucket = table.buckets[b]; if (bucket) { unsigned len = 1; hash_element_type *tb; total_buckets++; if (!summary_only) fprintf (stderr, "%4d ", b); for (tb = bucket->next; tb != NULL; tb = tb->next) len++; if (!summary_only) fprintf (stderr, ":%-5d", len); total_elements += len; if (!summary_only) { for (tb = bucket; tb != NULL; tb = tb->next) fprintf (stderr, " %s=>%s", tb->key, tb->value); putc ('\n', stderr); } } } fprintf (stderr, "%u buckets, %u nonempty (%u%%); %u entries, average chain %.1f.\n", table.size, total_buckets, 100 * total_buckets / table.size, total_elements, total_buckets ? total_elements / (double) total_buckets : 0.0); }