Tachyon (current)  Current Main Branch
hash.c
Go to the documentation of this file.
1 /*
2  * hash.c - This file contains code implementing various hash tables
3  *
4  * (C) Copyright 1994-2022 John E. Stone
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * $Id: hash.c,v 1.3 2022/02/18 17:55:28 johns Exp $
8  *
9  */
10 
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include "hash.h"
16 
17 #define HASH_LIMIT 0.5
18 
19 /*
20  * Local types
21  */
22 typedef struct hash_node_t {
23  int data; /* data in hash node */
24  const char * key; /* key for hash lookup */
25  struct hash_node_t *next; /* next node in hash chain */
26 } hash_node_t;
27 
28 /*
29  * hash() - Hash function returns a hash number for a given key.
30  *
31  * tptr: Pointer to a hash table
32  * key: The key to create a hash number for
33  */
34 static int hash(rt_hash_t *tptr, const char *key) {
35  int i=0;
36  int hashvalue;
37 
38  while (*key != '\0')
39  i=(i<<3)+(*key++ - '0');
40 
41  hashvalue = (((i*1103515249)>>tptr->downshift) & tptr->mask);
42  if (hashvalue < 0) {
43  hashvalue = 0;
44  }
45 
46  return hashvalue;
47 }
48 
49 /*
50  * rebuild_table() - Create new hash table when old one fills up.
51  *
52  * tptr: Pointer to a hash table
53  */
54 static void rebuild_table(rt_hash_t *tptr) {
55  hash_node_t **old_bucket, *old_hash, *tmp;
56  int old_size, h, i;
57 
58  old_bucket=tptr->bucket;
59  old_size=tptr->size;
60 
61  /* create a new table and rehash old buckets */
62  rt_hash_init(tptr, old_size<<1);
63  for (i=0; i<old_size; i++) {
64  old_hash=old_bucket[i];
65  while(old_hash) {
66  tmp=old_hash;
67  old_hash=old_hash->next;
68  h=hash(tptr, tmp->key);
69  tmp->next=tptr->bucket[h];
70  tptr->bucket[h]=tmp;
71  tptr->entries++;
72  } /* while */
73  } /* for */
74 
75  /* free memory used by old table */
76  free(old_bucket);
77 
78  return;
79 }
80 
81 /*
82  * rt_hash_init() - Initialize a new hash table.
83  *
84  * tptr: Pointer to the hash table to initialize
85  * buckets: The number of initial buckets to create
86  */
87 void rt_hash_init(rt_hash_t *tptr, int buckets) {
88 
89  /* make sure we allocate something */
90  if (buckets==0)
91  buckets=16;
92 
93  /* initialize the table */
94  tptr->entries=0;
95  tptr->size=2;
96  tptr->mask=1;
97  tptr->downshift=29;
98 
99  /* ensure buckets is a power of 2 */
100  while (tptr->size<buckets) {
101  tptr->size<<=1;
102  tptr->mask=(tptr->mask<<1)+1;
103  tptr->downshift--;
104  } /* while */
105 
106  /* allocate memory for table */
107  tptr->bucket=(hash_node_t **) calloc(tptr->size, sizeof(hash_node_t *));
108 
109  return;
110 }
111 
112 /*
113  * hash_lookup() - Lookup an entry in the hash table and return a
114  * pointer to it or HASH_FAIL if it wasn't found.
115  *
116  * tptr: Pointer to the hash table
117  * key: The key to lookup
118  */
119 int rt_hash_lookup(rt_hash_t *tptr, const char *key) {
120  int h;
121  hash_node_t *node;
122 
123 
124  /* find the entry in the hash table */
125  h=hash(tptr, key);
126  for (node=tptr->bucket[h]; node!=NULL; node=node->next) {
127  if (!strcmp(node->key, key))
128  break;
129  }
130 
131  /* return the entry if it exists, or HASH_FAIL */
132  return(node ? node->data : HASH_FAIL);
133 }
134 
135 /*
136  * rt_hash_insert() - Insert an entry into the hash table. If the
137  * entry already exists return a pointer to it,
138  * otherwise return HASH_FAIL.
139  *
140  * tptr: A pointer to the hash table
141  * key: The key to insert into the hash table
142  * data: A pointer to the data to insert into the hash table
143  */
144 int rt_hash_insert(rt_hash_t *tptr, const char *key, int data) {
145  int tmp;
146  hash_node_t *node;
147  int h;
148 
149 
150  /* check to see if the entry exists */
151  if ((tmp=rt_hash_lookup(tptr, key)) != HASH_FAIL)
152  return(tmp);
153 
154  /* expand the table if needed */
155  while (tptr->entries>=HASH_LIMIT*tptr->size)
156  rebuild_table(tptr);
157 
158  /* insert the new entry */
159  h=hash(tptr, key);
160  node=(struct hash_node_t *) malloc(sizeof(hash_node_t));
161  node->data=data;
162  node->key=key;
163  node->next=tptr->bucket[h];
164  tptr->bucket[h]=node;
165  tptr->entries++;
166 
167  return HASH_FAIL;
168 }
169 
170 /*
171  * rt_hash_delete() - Remove an entry from a hash table and return a pointer
172  * to its data or HASH_FAIL if it wasn't found.
173  *
174  * tptr: A pointer to the hash table
175  * key: The key to remove from the hash table
176  */
177 int rt_hash_delete(rt_hash_t *tptr, const char *key) {
178  hash_node_t *node, *last;
179  int data;
180  int h;
181 
182  /* find the node to remove */
183  h=hash(tptr, key);
184  for (node=tptr->bucket[h]; node; node=node->next) {
185  if (!strcmp(node->key, key))
186  break;
187  }
188 
189  /* Didn't find anything, return HASH_FAIL */
190  if (node==NULL)
191  return HASH_FAIL;
192 
193  /* if node is at head of bucket, we have it easy */
194  if (node==tptr->bucket[h])
195  tptr->bucket[h]=node->next;
196  else {
197  /* find the node before the node we want to remove */
198  for (last=tptr->bucket[h]; last && last->next; last=last->next) {
199  if (last->next==node)
200  break;
201  }
202  last->next=node->next;
203  }
204 
205  /* free memory and return the data */
206  data=node->data;
207  free(node);
208 
209  return(data);
210 }
211 
212 
213 
214 /*
215  * rt_hash_destroy() - Delete the entire table, and all remaining entries.
216  *
217  */
219  hash_node_t *node, *last;
220  int i;
221 
222  for (i=0; i<tptr->size; i++) {
223  node = tptr->bucket[i];
224  while (node != NULL) {
225  last = node;
226  node = node->next;
227  free(last);
228  }
229  }
230 
231  /* free the entire array of buckets */
232  if (tptr->bucket != NULL) {
233  free(tptr->bucket);
234  memset(tptr, 0, sizeof(rt_hash_t));
235  }
236 }
237 
238 /*
239  * alos() - Find the average length of search.
240  *
241  * tptr: Pointer to a hash table
242  */
243 static float alos(rt_hash_t *tptr) {
244  int i,j;
245  float alos=0;
246  hash_node_t *node;
247 
248 
249  for (i=0; i<tptr->size; i++) {
250  for (node=tptr->bucket[i], j=0; node!=NULL; node=node->next, j++);
251  if (j)
252  alos+=((j*(j+1))>>1);
253  } /* for */
254 
255  return(tptr->entries ? alos/tptr->entries : 0);
256 }
257 
258 
259 /*
260  * rt_hash_stats() - Return a string with stats about a hash table.
261  *
262  * tptr: A pointer to the hash table
263  */
264 char * rt_hash_stats(rt_hash_t *tptr) {
265  static char buf[1024];
266 
267  sprintf(buf, "%u slots, %u entries, and %1.2f ALOS",
268  (int)tptr->size, (int)tptr->entries, alos(tptr));
269 
270  return(buf);
271 }
272 
273 
274 
275 
int mask
used to select bits for hashing
Definition: hash.h:23
#define HASH_FAIL
Return code when a hash key is not find, or there&#39;s no collision upon insertion.
Definition: hash.h:30
void rt_hash_init(rt_hash_t *tptr, int buckets)
Definition: hash.c:87
void rt_hash_destroy(rt_hash_t *tptr)
Definition: hash.c:218
#define HASH_LIMIT
Definition: hash.c:17
struct hash_node_t ** bucket
array of hash nodes
Definition: hash.h:19
Definition: hash.h:18
int entries
number of entries in table
Definition: hash.h:21
struct hash_node_t * next
Definition: hash.c:25
int rt_hash_delete(rt_hash_t *tptr, const char *key)
Definition: hash.c:177
int data
Definition: hash.c:23
struct hash_node_t hash_node_t
static void rebuild_table(rt_hash_t *tptr)
Definition: hash.c:54
static float alos(rt_hash_t *tptr)
Definition: hash.c:243
int downshift
shift cound, used in hash function
Definition: hash.h:22
int rt_hash_insert(rt_hash_t *tptr, const char *key, int data)
Definition: hash.c:144
int size
size of the array
Definition: hash.h:20
char * rt_hash_stats(rt_hash_t *tptr)
Definition: hash.c:264
int rt_hash_lookup(rt_hash_t *tptr, const char *key)
Definition: hash.c:119
const char * key
Definition: hash.c:24
static int hash(rt_hash_t *tptr, const char *key)
Definition: hash.c:34