[project @ 2005-03-04 14:24:51 by simonmar]
[ghc-hetmet.git] / ghc / rts / Hash.c
1 /*-----------------------------------------------------------------------------
2  *
3  * (c) The AQUA Project, Glasgow University, 1995-1998
4  * (c) The GHC Team, 1999
5  *
6  * Dynamically expanding linear hash tables, as described in
7  * Per-\AAke Larson, ``Dynamic Hash Tables,'' CACM 31(4), April 1988,
8  * pp. 446 -- 457.
9  * -------------------------------------------------------------------------- */
10
11 #include "PosixSource.h"
12 #include "Rts.h"
13 #include "Hash.h"
14 #include "RtsUtils.h"
15
16 #include <stdlib.h>
17 #include <string.h>
18
19 #define HSEGSIZE    1024    /* Size of a single hash table segment */
20                             /* Also the minimum size of a hash table */
21 #define HDIRSIZE    1024    /* Size of the segment directory */
22                             /* Maximum hash table size is HSEGSIZE * HDIRSIZE */
23 #define HLOAD       5       /* Maximum average load of a single hash bucket */
24
25 #define HCHUNK      (1024 * sizeof(W_) / sizeof(HashList))
26                             /* Number of HashList cells to allocate in one go */
27
28
29 /* Linked list of (key, data) pairs for separate chaining */
30 struct hashlist {
31     StgWord key;
32     void *data;
33     struct hashlist *next;  /* Next cell in bucket chain (same hash value) */
34 };
35
36 typedef struct hashlist HashList;
37
38 typedef int HashFunction(HashTable *table, StgWord key);
39 typedef int CompareFunction(StgWord key1, StgWord key2);
40
41 struct hashtable {
42     int split;              /* Next bucket to split when expanding */
43     int max;                /* Max bucket of smaller table */
44     int mask1;              /* Mask for doing the mod of h_1 (smaller table) */
45     int mask2;              /* Mask for doing the mod of h_2 (larger table) */
46     int kcount;             /* Number of keys */
47     int bcount;             /* Number of buckets */
48     HashList **dir[HDIRSIZE];   /* Directory of segments */
49     HashFunction *hash;         /* hash function */
50     CompareFunction *compare;   /* key comparison function */
51 };
52
53 /* -----------------------------------------------------------------------------
54  * Hash first using the smaller table.  If the bucket is less than the
55  * next bucket to be split, re-hash using the larger table.
56  * -------------------------------------------------------------------------- */
57
58 static int
59 hashWord(HashTable *table, StgWord key)
60 {
61     int bucket;
62
63     /* Strip the boring zero bits */
64     key /= sizeof(StgWord);
65
66     /* Mod the size of the hash table (a power of 2) */
67     bucket = key & table->mask1;
68
69     if (bucket < table->split) {
70         /* Mod the size of the expanded hash table (also a power of 2) */
71         bucket = key & table->mask2;
72     }
73     return bucket;
74 }
75
76 static int
77 hashStr(HashTable *table, char *key)
78 {
79     int h, bucket;
80     char *s;
81
82     s = key;
83     for (h=0; *s; s++) {
84         h *= 128;
85         h += *s;
86         h = h % 1048583;        /* some random large prime */
87     }
88
89     /* Mod the size of the hash table (a power of 2) */
90     bucket = h & table->mask1;
91
92     if (bucket < table->split) {
93         /* Mod the size of the expanded hash table (also a power of 2) */
94         bucket = h & table->mask2;
95     }
96
97     return bucket;
98 }
99
100 static int
101 compareWord(StgWord key1, StgWord key2)
102 {
103     return (key1 == key2);
104 }
105
106 static int
107 compareStr(StgWord key1, StgWord key2)
108 {
109     return (strcmp((char *)key1, (char *)key2) == 0);
110 }
111
112
113 /* -----------------------------------------------------------------------------
114  * Allocate a new segment of the dynamically growing hash table.
115  * -------------------------------------------------------------------------- */
116
117 static void
118 allocSegment(HashTable *table, int segment)
119 {
120     table->dir[segment] = stgMallocBytes(HSEGSIZE * sizeof(HashList *), 
121                                          "allocSegment");
122 }
123
124
125 /* -----------------------------------------------------------------------------
126  * Expand the larger hash table by one bucket, and split one bucket
127  * from the smaller table into two parts.  Only the bucket referenced
128  * by @table->split@ is affected by the expansion.
129  * -------------------------------------------------------------------------- */
130
131 static void
132 expand(HashTable *table)
133 {
134     int oldsegment;
135     int oldindex;
136     int newbucket;
137     int newsegment;
138     int newindex;
139     HashList *hl;
140     HashList *next;
141     HashList *old, *new;
142
143     if (table->split + table->max >= HDIRSIZE * HSEGSIZE)
144         /* Wow!  That's big.  Too big, so don't expand. */
145         return;
146
147     /* Calculate indices of bucket to split */
148     oldsegment = table->split / HSEGSIZE;
149     oldindex = table->split % HSEGSIZE;
150
151     newbucket = table->max + table->split;
152
153     /* And the indices of the new bucket */
154     newsegment = newbucket / HSEGSIZE;
155     newindex = newbucket % HSEGSIZE;
156
157     if (newindex == 0)
158         allocSegment(table, newsegment);
159
160     if (++table->split == table->max) {
161         table->split = 0;
162         table->max *= 2;
163         table->mask1 = table->mask2;
164         table->mask2 = table->mask2 << 1 | 1;
165     }
166     table->bcount++;
167
168     /* Split the bucket, paying no attention to the original order */
169
170     old = new = NULL;
171     for (hl = table->dir[oldsegment][oldindex]; hl != NULL; hl = next) {
172         next = hl->next;
173         if (table->hash(table, hl->key) == newbucket) {
174             hl->next = new;
175             new = hl;
176         } else {
177             hl->next = old;
178             old = hl;
179         }
180     }
181     table->dir[oldsegment][oldindex] = old;
182     table->dir[newsegment][newindex] = new;
183
184     return;
185 }
186
187 void *
188 lookupHashTable(HashTable *table, StgWord key)
189 {
190     int bucket;
191     int segment;
192     int index;
193     HashList *hl;
194
195     bucket = table->hash(table, key);
196     segment = bucket / HSEGSIZE;
197     index = bucket % HSEGSIZE;
198
199     for (hl = table->dir[segment][index]; hl != NULL; hl = hl->next)
200         if (table->compare(hl->key, key))
201             return hl->data;
202
203     /* It's not there */
204     return NULL;
205 }
206
207 /* -----------------------------------------------------------------------------
208  * We allocate the hashlist cells in large chunks to cut down on malloc
209  * overhead.  Although we keep a free list of hashlist cells, we make
210  * no effort to actually return the space to the malloc arena.
211  * -------------------------------------------------------------------------- */
212
213 static HashList *freeList = NULL;
214
215 static HashList *
216 allocHashList(void)
217 {
218     HashList *hl, *p;
219
220     if ((hl = freeList) != NULL) {
221         freeList = hl->next;
222     } else {
223         hl = stgMallocBytes(HCHUNK * sizeof(HashList), "allocHashList");
224
225         freeList = hl + 1;
226         for (p = freeList; p < hl + HCHUNK - 1; p++)
227             p->next = p + 1;
228         p->next = NULL;
229     }
230     return hl;
231 }
232
233 static void
234 freeHashList(HashList *hl)
235 {
236     hl->next = freeList;
237     freeList = hl;
238 }
239
240 void
241 insertHashTable(HashTable *table, StgWord key, void *data)
242 {
243     int bucket;
244     int segment;
245     int index;
246     HashList *hl;
247
248     /* We want no duplicates */
249     ASSERT(lookupHashTable(table, key) == NULL);
250
251     /* When the average load gets too high, we expand the table */
252     if (++table->kcount >= HLOAD * table->bcount)
253         expand(table);
254
255     bucket = table->hash(table, key);
256     segment = bucket / HSEGSIZE;
257     index = bucket % HSEGSIZE;
258
259     hl = allocHashList();
260
261     hl->key = key;
262     hl->data = data;
263     hl->next = table->dir[segment][index];
264     table->dir[segment][index] = hl;
265
266 }
267
268 void *
269 removeHashTable(HashTable *table, StgWord key, void *data)
270 {
271     int bucket;
272     int segment;
273     int index;
274     HashList *hl;
275     HashList *prev = NULL;
276
277     bucket = table->hash(table, key);
278     segment = bucket / HSEGSIZE;
279     index = bucket % HSEGSIZE;
280
281     for (hl = table->dir[segment][index]; hl != NULL; hl = hl->next) {
282         if (table->compare(hl->key,key) && (data == NULL || hl->data == data)) {
283             if (prev == NULL)
284                 table->dir[segment][index] = hl->next;
285             else
286                 prev->next = hl->next;
287             freeHashList(hl);
288             table->kcount--;
289             return hl->data;
290         }
291         prev = hl;
292     }
293
294     /* It's not there */
295     ASSERT(data == NULL);
296     return NULL;
297 }
298
299 /* -----------------------------------------------------------------------------
300  * When we free a hash table, we are also good enough to free the
301  * data part of each (key, data) pair, as long as our caller can tell
302  * us how to do it.
303  * -------------------------------------------------------------------------- */
304
305 void
306 freeHashTable(HashTable *table, void (*freeDataFun)(void *) )
307 {
308     long segment;
309     long index;
310     HashList *hl;
311     HashList *next;
312
313     /* The last bucket with something in it is table->max + table->split - 1 */
314     segment = (table->max + table->split - 1) / HSEGSIZE;
315     index = (table->max + table->split - 1) % HSEGSIZE;
316
317     while (segment >= 0) {
318         while (index >= 0) {
319             for (hl = table->dir[segment][index]; hl != NULL; hl = next) {
320                 next = hl->next;
321                 if (freeDataFun != NULL)
322                     (*freeDataFun)(hl->data);
323                 freeHashList(hl);
324             }
325             index--;
326         }
327         stgFree(table->dir[segment]);
328         segment--;
329         index = HSEGSIZE - 1;
330     }
331     stgFree(table);
332 }
333
334 /* -----------------------------------------------------------------------------
335  * When we initialize a hash table, we set up the first segment as well,
336  * initializing all of the first segment's hash buckets to NULL.
337  * -------------------------------------------------------------------------- */
338
339 static HashTable *
340 allocHashTable_(HashFunction *hash, CompareFunction *compare)
341 {
342     HashTable *table;
343     HashList **hb;
344
345     table = stgMallocBytes(sizeof(HashTable),"allocHashTable");
346
347     allocSegment(table, 0);
348
349     for (hb = table->dir[0]; hb < table->dir[0] + HSEGSIZE; hb++)
350         *hb = NULL;
351
352     table->split = 0;
353     table->max = HSEGSIZE;
354     table->mask1 = HSEGSIZE - 1;
355     table->mask2 = 2 * HSEGSIZE - 1;
356     table->kcount = 0;
357     table->bcount = HSEGSIZE;
358     table->hash = hash;
359     table->compare = compare;
360
361     return table;
362 }
363
364 HashTable *
365 allocHashTable(void)
366 {
367     return allocHashTable_(hashWord, compareWord);
368 }
369
370 HashTable *
371 allocStrHashTable(void)
372 {
373     return allocHashTable_((HashFunction *)hashStr, 
374                            (CompareFunction *)compareStr);
375 }