| /* Implement simple hashing table with string based keys. |
| Copyright (C) 1994-2014 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, October 1994. |
| |
| 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; version 2 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 <http://www.gnu.org/licenses/>. */ |
| |
| #include "simple-hash.h" |
| |
| #include <obstack.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <values.h> |
| |
| #define obstack_chunk_alloc malloc |
| #define obstack_chunk_free free |
| |
| #ifndef BITSPERBYTE |
| #define BITSPERBYTE 8 |
| #endif |
| |
| #ifndef bcopy |
| #define bcopy(s, d, n) memcpy((d), (s), (n)) |
| #endif |
| |
| #define hashval_t uint32_t |
| #include "third_party/glibc_locales/common/hashval.h" |
| #include "third_party/glibc_locales/programs/xmalloc.h" |
| |
| typedef struct hash_entry { |
| unsigned long used; |
| const void *key; |
| size_t keylen; |
| void *data; |
| struct hash_entry *next; |
| } hash_entry; |
| |
| /* Prototypes for local functions. */ |
| static void insert_entry_2(hash_table *htab, const void *key, size_t keylen, |
| unsigned long hval, size_t idx, void *data); |
| static size_t lookup(const hash_table *htab, const void *key, size_t keylen, |
| unsigned long int hval); |
| static int is_prime(unsigned long int candidate); |
| |
| int init_hash(htab, init_size) |
| hash_table *htab; |
| unsigned long int init_size; |
| { |
| /* We need the size to be a prime. */ |
| init_size = next_prime(init_size); |
| |
| /* Initialize the data structure. */ |
| htab->size = init_size; |
| htab->filled = 0; |
| htab->first = NULL; |
| htab->table = (void *)xcalloc(init_size + 1, sizeof(hash_entry)); |
| if (htab->table == NULL) return -1; |
| |
| obstack_init(&htab->mem_pool); |
| |
| return 0; |
| } |
| |
| int delete_hash(htab) |
| hash_table *htab; |
| { |
| free(htab->table); |
| obstack_free(&htab->mem_pool, NULL); |
| return 0; |
| } |
| |
| int insert_entry(htab, key, keylen, data) |
| hash_table *htab; |
| const void *key; |
| size_t keylen; |
| void *data; |
| { |
| unsigned long int hval = compute_hashval(key, keylen); |
| hash_entry *table = (hash_entry *)htab->table; |
| size_t idx = lookup(htab, key, keylen, hval); |
| |
| if (table[idx].used) /* We don't want to overwrite the old value. */ |
| return -1; |
| else { |
| /* An empty bucket has been found. */ |
| insert_entry_2(htab, obstack_copy(&htab->mem_pool, key, keylen), keylen, |
| hval, idx, data); |
| return 0; |
| } |
| } |
| |
| static void insert_entry_2(htab, key, keylen, hval, idx, data) hash_table *htab; |
| const void *key; |
| size_t keylen; |
| unsigned long int hval; |
| size_t idx; |
| void *data; |
| { |
| hash_entry *table = (hash_entry *)htab->table; |
| |
| table[idx].used = hval; |
| table[idx].key = key; |
| table[idx].keylen = keylen; |
| table[idx].data = data; |
| |
| /* List the new value in the list. */ |
| if ((hash_entry *)htab->first == NULL) { |
| table[idx].next = &table[idx]; |
| htab->first = &table[idx]; |
| } else { |
| table[idx].next = ((hash_entry *)htab->first)->next; |
| ((hash_entry *)htab->first)->next = &table[idx]; |
| htab->first = &table[idx]; |
| } |
| |
| ++htab->filled; |
| if (100 * htab->filled > 75 * htab->size) { |
| /* Table is filled more than 75%. Resize the table. |
| Experiments have shown that for best performance, this threshold |
| must lie between 40% and 85%. */ |
| unsigned long int old_size = htab->size; |
| |
| htab->size = next_prime(htab->size * 2); |
| htab->filled = 0; |
| htab->first = NULL; |
| htab->table = (void *)xcalloc(1 + htab->size, sizeof(hash_entry)); |
| |
| for (idx = 1; idx <= old_size; ++idx) |
| if (table[idx].used) |
| insert_entry_2( |
| htab, table[idx].key, table[idx].keylen, table[idx].used, |
| lookup(htab, table[idx].key, table[idx].keylen, table[idx].used), |
| table[idx].data); |
| |
| free(table); |
| } |
| } |
| |
| int find_entry(htab, key, keylen, result) const hash_table *htab; |
| const void *key; |
| size_t keylen; |
| void **result; |
| { |
| hash_entry *table = (hash_entry *)htab->table; |
| size_t idx = lookup(htab, key, keylen, compute_hashval(key, keylen)); |
| |
| if (table[idx].used == 0) return -1; |
| |
| *result = table[idx].data; |
| return 0; |
| } |
| |
| int set_entry(htab, key, keylen, newval) |
| hash_table *htab; |
| const void *key; |
| size_t keylen; |
| void *newval; |
| { |
| hash_entry *table = (hash_entry *)htab->table; |
| size_t idx = lookup(htab, key, keylen, compute_hashval(key, keylen)); |
| |
| if (table[idx].used == 0) return -1; |
| |
| table[idx].data = newval; |
| return 0; |
| } |
| |
| int iterate_table(htab, ptr, key, keylen, data) const hash_table *htab; |
| void **ptr; |
| const void **key; |
| size_t *keylen; |
| void **data; |
| { |
| if (*ptr == NULL) { |
| if (htab->first == NULL) return -1; |
| *ptr = (void *)((hash_entry *)htab->first)->next; |
| } else { |
| if (*ptr == htab->first) return -1; |
| *ptr = (void *)(((hash_entry *)*ptr)->next); |
| } |
| |
| *key = ((hash_entry *)*ptr)->key; |
| *keylen = ((hash_entry *)*ptr)->keylen; |
| *data = ((hash_entry *)*ptr)->data; |
| return 0; |
| } |
| |
| /* References: |
| [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 |
| [Knuth] The Art of Computer Programming, part3 (6.4) */ |
| |
| static size_t lookup(htab, key, keylen, hval) const hash_table *htab; |
| const void *key; |
| size_t keylen; |
| unsigned long int hval; |
| { |
| unsigned long int hash; |
| size_t idx; |
| hash_entry *table = (hash_entry *)htab->table; |
| |
| /* First hash function: simply take the modul but prevent zero. */ |
| hash = 1 + hval % htab->size; |
| |
| idx = hash; |
| |
| if (table[idx].used) { |
| if (table[idx].used == hval && table[idx].keylen == keylen && |
| memcmp(table[idx].key, key, keylen) == 0) |
| return idx; |
| |
| /* Second hash function as suggested in [Knuth]. */ |
| hash = 1 + hval % (htab->size - 2); |
| |
| do { |
| if (idx <= hash) |
| idx = htab->size + idx - hash; |
| else |
| idx -= hash; |
| |
| /* If entry is found use it. */ |
| if (table[idx].used == hval && table[idx].keylen == keylen && |
| memcmp(table[idx].key, key, keylen) == 0) |
| return idx; |
| } while (table[idx].used); |
| } |
| return idx; |
| } |
| |
| unsigned long int next_prime(seed) |
| unsigned long int seed; |
| { |
| /* Make it definitely odd. */ |
| seed |= 1; |
| |
| while (!is_prime(seed)) seed += 2; |
| |
| return seed; |
| } |
| |
| static int is_prime(candidate) |
| unsigned long int candidate; |
| { |
| /* No even number and none less than 10 will be passed here. */ |
| unsigned long int divn = 3; |
| unsigned long int sq = divn * divn; |
| |
| while (sq < candidate && candidate % divn != 0) { |
| ++divn; |
| sq += 4 * divn; |
| ++divn; |
| } |
| |
| return candidate % divn != 0; |
| } |