Checks now the Load_mode
[libfirm] / ir / adt / set.c
1 /*
2  * Project:     libFIRM
3  * File name:   ir/adt/set.c
4  * Purpose:     Set --- collection of entries that are unique wrt to a key.
5  * Author:      Markus Armbruster
6  * Modified by:
7  * Created:     1999 by getting from fiasco
8  * CVS-ID:      $Id$
9  * Copyright:   (c) 1995, 1996 Markus Armbruster
10  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
11  */
12
13 /*  This code is derived from:
14
15     From: ejp@ausmelb.oz.AU (Esmond Pitt)
16     Date: Tue, 7 Mar 1989 22:06:26 GMT
17     Subject: v06i042: dynamic hashing version of hsearch(3)
18     Message-ID: <1821@basser.oz>
19     Newsgroups: comp.sources.misc
20     Sender: msgs@basser.oz
21
22     Posting-number: Volume 6, Issue 42
23     Submitted-By: Esmond Pitt <ejp@ausmelb.oz.AU>
24     Archive-name: dynamic-hash
25
26     * Dynamic hashing, after CACM April 1988 pp 446-457, by Per-Ake Larson.
27     * Coded into C, with minor code improvements, and with hsearch(3) interface,
28     * by ejp@ausmelb.oz, Jul 26, 1988: 13:16;
29
30     TODO: Fix Esmond's ugly MixedCapsIdentifiers ;->
31  */
32
33 /* $Id$ */
34
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
38
39 /* bcopy is not ISO C *
40 #define bcopy(X, Y, Z) memcpy((Y), (X), (Z))
41 */
42
43 #ifdef PSET
44 # define SET pset
45 # define PMANGLE(pre) pre##_pset
46 # define MANGLEP(post) pset_##post
47 # define MANGLE(pre, post) pre##pset##post
48 # define EQUAL(cmp, elt, key, siz) (!(cmp) ((elt)->entry.dptr, (key)))
49 #else
50 # define SET set
51 # define PMANGLE(pre) pre##_set
52 # define MANGLEP(post) set_##post
53 # define MANGLE(pre, post) pre##set##post
54 # define EQUAL(cmp, elt, key, siz) \
55     (((elt)->entry.size == (siz)) && !(cmp) ((elt)->entry.dptr, (key), (siz)))
56 #endif
57
58 #include <assert.h>
59 #include <stdlib.h>
60 #include <stdio.h>
61 #include <string.h>
62 #include "xmalloc.h"
63 #ifdef PSET
64 # include "pset.h"
65 #else
66 # include "set.h"
67 #endif
68
69
70 #define TOBSTACK_ID MANGLEP(tag)
71 #include "obst.h"
72
73
74 #define SEGMENT_SIZE_SHIFT      8
75 #define SEGMENT_SIZE            (1 << SEGMENT_SIZE_SHIFT)
76 #define DIRECTORY_SIZE_SHIFT    8
77 #define DIRECTORY_SIZE          (1 << DIRECTORY_SIZE_SHIFT)
78 #define MAX_LOAD_FACTOR         4
79
80
81 typedef struct element {
82   struct element *chain;        /* for chaining Elements */
83   MANGLEP (entry) entry;
84 } Element, *Segment;
85
86
87 struct SET {
88   short p;                      /* Next bucket to be split      */
89   short maxp;                   /* upper bound on p during expansion    */
90   int nkey;                     /* current # keys       */
91   short nseg;                   /* current # segments   */
92   Segment *dir[DIRECTORY_SIZE];
93   MANGLEP(cmp_fun) cmp;         /* function comparing entries */
94   int iter_i, iter_j;
95   Element *iter_tail;           /* non-NULL while iterating over elts */
96 #ifdef PSET
97   Element *free_list;           /* list of free Elements */
98 #endif
99   struct obstack obst;          /* obstack for allocation all data */
100 #ifdef STATS
101   int naccess, ncollision, ndups;
102   int max_chain_len;
103 #endif
104 #ifdef DEBUG
105   const char *tag;
106 #endif
107 };
108
109
110 #ifdef STATS
111
112 void
113 MANGLEP(stats) (SET *table)
114 {
115   int nfree = 0;
116 #ifdef PSET
117   Element *q = table->free_list;
118   while (q) { q = q->chain; ++nfree; }
119 #endif
120   printf ("     accesses  collisions        keys  duplicates     longest      wasted\n%12d%12d%12d%12d%12d%12d\n",
121           table->naccess, table->ncollision, table->nkey, table->ndups, table->max_chain_len, nfree);
122 }
123
124 static INLINE void
125 stat_chain_len (SET *table, int chain_len)
126 {
127   table->ncollision += chain_len;
128   if (table->max_chain_len < chain_len) table->max_chain_len = chain_len;
129 }
130
131 # define stat_access(table) (++(table)->naccess)
132 # define stat_dup(table) (++(table)->ndups)
133
134 #else /* !STATS */
135
136 # define stat_chain_len(table, chain_len) ((void)0)
137 # define stat_access(table) ((void)0)
138 # define stat_dup(table) ((void)0)
139
140 #endif /* !STATS */
141
142 #ifdef DEBUG
143
144 const char *MANGLEP(tag);
145
146
147 static void
148 MANGLEP(describe) (SET *table)
149 {
150   int i, j, collide;
151   Element *ptr;
152   Segment *seg;
153
154   printf ("p=%d maxp=%d nkey=%d nseg=%d\n",
155           table->p, table->maxp, table->nkey, table->nseg);
156   for (i = 0;  i < table->nseg;  i++) {
157     seg = table->dir[i];
158     for (j = 0;  j < SEGMENT_SIZE;  j++) {
159       collide = 0;
160       ptr = seg[j];
161       while (ptr) {
162         if (collide) printf ("<%3d>", collide);
163         else printf ("table");
164         printf ("[%d][%3d]: %u %p\n", i, j, ptr->entry.hash, ptr->entry.dptr);
165         ptr = ptr->chain;
166         collide++;
167       }
168     }
169   }
170 }
171
172 #endif /* !DEBUG */
173
174
175 SET *
176 (PMANGLE(new)) (MANGLEP(cmp_fun) cmp, int nslots)
177 {
178   int i;
179   SET *table = xmalloc (sizeof (SET));
180
181   /* Adjust nslots up to next power of 2, minimum SEGMENT_SIZE */
182   assert (nslots >= 0);
183   for (i = SEGMENT_SIZE;  i < nslots;  i <<= 1) assert (i < (i << 1));
184   nslots = i >> SEGMENT_SIZE_SHIFT;
185
186   table->nseg = table->p = table->nkey = 0;
187   table->maxp = nslots << SEGMENT_SIZE_SHIFT;
188   table->cmp = cmp;
189   table->iter_tail = NULL;
190 #ifdef PSET
191   table->free_list = NULL;
192 #endif
193   obstack_init (&table->obst);
194
195   /* Make segments */
196   for (i = 0;  i < nslots;  ++i) {
197     table->dir[i] = (Segment *)obstack_alloc (&table->obst,
198                                               sizeof (Segment) * SEGMENT_SIZE);
199
200     memset(table->dir[i], 0, sizeof (Segment) * SEGMENT_SIZE);
201     table->nseg++;
202   }
203
204 #ifdef STATS
205   table->naccess = table->ncollision = table->ndups = 0;
206   table->max_chain_len = 0;
207 #endif
208 #ifdef DEBUG
209   table->tag = MANGLEP(tag);
210 #endif
211   return table;
212 }
213
214
215 void
216 PMANGLE(del) (SET *table)
217 {
218 #ifdef DEBUG
219   MANGLEP(tag) = table->tag;
220 #endif
221   obstack_free (&table->obst, NULL);
222   xfree (table);
223 }
224
225 int
226 MANGLEP(count) (SET *table)
227 {
228   return table->nkey;
229 }
230
231 /*
232  * do one iteration step, return 1
233  * if still data in the set, 0 else
234  */
235 static INLINE int
236 iter_step (SET *table)
237 {
238   if (++table->iter_j >= SEGMENT_SIZE) {
239     table->iter_j = 0;
240     if (++table->iter_i >= table->nseg) {
241       table->iter_i = 0;
242       return 0;
243     }
244   }
245   return 1;
246 }
247
248 /*
249  * finds the first entry in the table
250  */
251 void *
252 MANGLEP(first) (SET *table)
253 {
254   assert (!table->iter_tail);
255   table->iter_i = 0;
256   table->iter_j = 0;
257   while (!table->dir[table->iter_i][table->iter_j]) {
258     if (!iter_step (table)) return NULL;
259   }
260   table->iter_tail = table->dir[table->iter_i][table->iter_j];
261   assert (table->iter_tail->entry.dptr);
262   return table->iter_tail->entry.dptr;
263 }
264
265 /*
266  * returns next entry in the table
267  */
268 void *
269 MANGLEP(next) (SET *table)
270 {
271   if (!table->iter_tail)
272     return NULL;
273
274   /* follow collision chain */
275   table->iter_tail = table->iter_tail->chain;
276   if (!table->iter_tail) {
277     /* go to next segment */
278     do {
279       if (!iter_step (table)) return NULL;
280     } while (!table->dir[table->iter_i][table->iter_j]);
281     table->iter_tail = table->dir[table->iter_i][table->iter_j];
282   }
283   assert (table->iter_tail->entry.dptr);
284   return table->iter_tail->entry.dptr;
285 }
286
287 void
288 MANGLEP(break) (SET *table)
289 {
290   table->iter_tail = NULL;
291 }
292
293 /*
294  * limit the hash value
295  */
296 static INLINE unsigned
297 Hash (SET *table, unsigned h)
298 {
299   unsigned address;
300
301   address = h & (table->maxp - 1);          /* h % table->maxp */
302   if (address < (unsigned)table->p)
303     address = h & ((table->maxp << 1) - 1); /* h % (2*table->maxp) */
304   return address;
305 }
306
307 /*
308  * returns non-zero if the number of elements in
309  * the set is greater then number of segments * MAX_LOAD_FACTOR
310  */
311 static INLINE int
312 loaded (SET *table)
313 {
314   return (  ++table->nkey
315           > (table->nseg << SEGMENT_SIZE_SHIFT) * MAX_LOAD_FACTOR);
316 }
317
318 /*
319  * expand the hash-table: the algorithm is split, so on every
320  * insert, only ONE segment is rehashed!
321  *
322  * table->p contains the current segment to split
323  * after all segments were split, table->p is set to zero and
324  * table->maxp is duplicated.
325  */
326 static void
327 expand_table (SET *table)
328 {
329   unsigned NewAddress;
330   int OldSegmentIndex, NewSegmentIndex;
331   int OldSegmentDir, NewSegmentDir;
332   Segment *OldSegment;
333   Segment *NewSegment;
334   Element *Current;
335   Element **Previous;
336   Element **LastOfNew;
337
338   if (table->maxp + table->p < (DIRECTORY_SIZE << SEGMENT_SIZE_SHIFT)) {
339     /* Locate the bucket to be split */
340     OldSegmentDir   = table->p >> SEGMENT_SIZE_SHIFT;
341     OldSegment      = table->dir[OldSegmentDir];
342     OldSegmentIndex = table->p & (SEGMENT_SIZE-1);
343
344     /* Expand address space; if necessary create a new segment */
345     NewAddress      = table->maxp + table->p;
346     NewSegmentDir   = NewAddress >> SEGMENT_SIZE_SHIFT;
347     NewSegmentIndex = NewAddress & (SEGMENT_SIZE-1);
348     if (NewSegmentIndex == 0) {
349       table->dir[NewSegmentDir] =
350         (Segment *)obstack_alloc (&table->obst,
351                                   sizeof(Segment) * SEGMENT_SIZE);
352       memset(table->dir[NewSegmentDir], 0, sizeof(Segment) * SEGMENT_SIZE);
353       table->nseg++;
354     }
355     NewSegment = table->dir[NewSegmentDir];
356
357     /* Adjust state variables */
358     table->p++;
359     if (table->p == table->maxp) {
360       table->maxp <<= 1;        /* table->maxp *= 2     */
361       table->p = 0;
362     }
363
364     /* Relocate records to the new bucket */
365     Previous = &OldSegment[OldSegmentIndex];
366     Current = *Previous;
367     LastOfNew = &NewSegment[NewSegmentIndex];
368     *LastOfNew = NULL;
369     while (Current != NULL) {
370       if (Hash (table, Current->entry.hash) == NewAddress) {
371         /* move to new chain */
372         *LastOfNew = Current;
373         *Previous  = Current->chain;
374         LastOfNew  = &Current->chain;
375         Current    = Current->chain;
376         *LastOfNew = NULL;
377       } else {
378         /* leave on old chain */
379         Previous = &Current->chain;
380         Current = Current->chain;
381       }
382     }
383   }
384 }
385
386
387 void *
388 MANGLE(_,_search) (SET *table,
389                    const void *key,
390 #ifndef PSET
391                    size_t size,
392 #endif
393                    unsigned hash,
394                    MANGLE(_,_action) action)
395 {
396   unsigned h;
397   Segment *CurrentSegment;
398   int SegmentIndex;
399   MANGLEP(cmp_fun) cmp = table->cmp;
400   Segment q;
401   int chain_len = 0;
402
403   assert (table);
404   assert (!table->iter_tail);
405   assert (key);
406 #ifdef DEBUG
407   MANGLEP(tag) = table->tag;
408 #endif
409   stat_access (table);
410
411   /* Find collision chain */
412   h = Hash (table, hash);
413   SegmentIndex   = h & (SEGMENT_SIZE-1);
414   CurrentSegment = table->dir[h >> SEGMENT_SIZE_SHIFT];
415   assert (CurrentSegment != NULL);
416   q = CurrentSegment[SegmentIndex];
417
418   /* Follow collision chain */
419   while (q && !EQUAL (cmp, q, key, size)) {
420     q = q->chain;
421     ++chain_len;
422   }
423
424   stat_chain_len (table, chain_len);
425
426   if (!q && (action != MANGLE(_,_find))) { /* not found, insert */
427     if (CurrentSegment[SegmentIndex]) stat_dup (table);
428
429 #ifdef PSET
430     if (table->free_list) {
431       q = table->free_list;
432       table->free_list = table->free_list->chain;
433     } else {
434       q = obstack_alloc (&table->obst, sizeof (Element));
435     }
436     q->entry.dptr = (void *)key;
437 #else
438     obstack_blank (&table->obst, offsetof (Element, entry.dptr));
439     if (action == _set_hinsert0)
440       obstack_grow0 (&table->obst, key, size);
441     else
442       obstack_grow (&table->obst, key, size);
443     q = obstack_finish (&table->obst);
444     q->entry.size = size;
445 #endif
446     q->chain = CurrentSegment[SegmentIndex];
447     q->entry.hash = hash;
448     CurrentSegment[SegmentIndex] = q;
449
450     if (loaded (table)) {
451       expand_table(table);      /* doesn't affect q */
452     }
453   }
454
455   if (!q) return NULL;
456 #ifdef PSET
457   if (action == _pset_hinsert) return &q->entry;
458 #else
459   if (action == _set_hinsert || action == _set_hinsert0) return &q->entry;
460 #endif
461   return q->entry.dptr;
462 }
463
464
465 #ifdef PSET
466
467 void *
468 pset_remove (SET *table, const void *key, unsigned hash)
469 {
470   unsigned h;
471   Segment *CurrentSegment;
472   int SegmentIndex;
473   pset_cmp_fun cmp = table->cmp;
474   Segment *p;
475   Segment q;
476   int chain_len = 0;
477
478   assert (table && !table->iter_tail);
479   stat_access (table);
480
481   /* Find collision chain */
482   h = Hash (table, hash);
483   SegmentIndex = h & (SEGMENT_SIZE-1);
484   CurrentSegment = table->dir[h >> SEGMENT_SIZE_SHIFT];
485   assert (CurrentSegment != NULL);
486   p = &CurrentSegment[SegmentIndex];
487
488   /* Follow collision chain */
489   while (!EQUAL (cmp, *p, key, size)) {
490     p = &(*p)->chain;
491     assert (*p);
492     ++chain_len;
493   }
494
495   stat_chain_len (table, chain_len);
496
497   q = *p;
498
499   if (q == table->iter_tail) {
500     /* removing current element */
501     table->iter_tail = q->chain;
502     if (!table->iter_tail) {
503       /* go to next segment */
504       do {
505         if (!iter_step (table))
506           break;
507       } while (!table->dir[table->iter_i][table->iter_j]);
508       table->iter_tail = table->dir[table->iter_i][table->iter_j];
509     }
510   }
511
512   *p = (*p)->chain;
513   q->chain = table->free_list;
514   table->free_list = q;
515   --table->nkey;
516
517   return q->entry.dptr;
518 }
519
520
521 void *
522 (pset_find) (SET *se, const void *key, unsigned hash)
523 {
524   return pset_find (se, key, hash);
525 }
526
527
528 void *
529 (pset_insert) (SET *se, const void *key, unsigned hash)
530 {
531   return pset_insert (se, key, hash);
532 }
533
534
535 MANGLEP(entry) *
536 (pset_hinsert) (SET *se, const void *key, unsigned hash)
537 {
538   return pset_hinsert (se, key, hash);
539 }
540
541 #else /* !PSET */
542
543 void *
544 (set_find) (set *se, const void *key, size_t size, unsigned hash)
545 {
546   return set_find (se, key, size, hash);
547 }
548
549
550 void *
551 (set_insert) (set *se, const void *key, size_t size, unsigned hash)
552 {
553   return set_insert (se, key, size, hash);
554 }
555
556
557 set_entry *
558 (set_hinsert) (set *se, const void *key, size_t size, unsigned hash)
559 {
560   return set_hinsert (se, key, size, hash);
561 }
562
563 #endif /* !PSET */