fix
[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   unsigned p;                   /* Next bucket to be split      */
89   unsigned maxp;                /* upper bound on p during expansion    */
90   unsigned nkey;                /* current # keys       */
91   unsigned nseg;                /* current # segments   */
92   Segment *dir[DIRECTORY_SIZE];
93   MANGLEP(cmp_fun) cmp;         /* function comparing entries */
94   unsigned 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 void
148 MANGLEP(describe) (SET *table)
149 {
150   unsigned i, j, collide;
151   Element *ptr;
152   Segment *seg;
153
154   printf ("p=%u maxp=%u nkey=%u nseg=%u\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, (void *)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   if (nslots > SEGMENT_SIZE * DIRECTORY_SIZE)
182     n_slots = DIRECTORY_SIZE;
183   else {
184     assert (nslots >= 0);
185     /* Adjust nslots up to next power of 2, minimum SEGMENT_SIZE */
186     for (i = SEGMENT_SIZE;  i < nslots;  i <<= 1);
187     nslots = i >> SEGMENT_SIZE_SHIFT;
188   }
189
190   table->nseg = table->p = table->nkey = 0;
191   table->maxp = nslots << SEGMENT_SIZE_SHIFT;
192   table->cmp = cmp;
193   table->iter_tail = NULL;
194 #ifdef PSET
195   table->free_list = NULL;
196 #endif
197   obstack_init (&table->obst);
198
199   /* Make segments */
200   for (i = 0;  i < nslots;  ++i) {
201     table->dir[i] = (Segment *)obstack_alloc (&table->obst,
202                                               sizeof (Segment) * SEGMENT_SIZE);
203
204     memset(table->dir[i], 0, sizeof (Segment) * SEGMENT_SIZE);
205     table->nseg++;
206   }
207
208 #ifdef STATS
209   table->naccess = table->ncollision = table->ndups = 0;
210   table->max_chain_len = 0;
211 #endif
212 #ifdef DEBUG
213   table->tag = MANGLEP(tag);
214 #endif
215   return table;
216 }
217
218
219 void
220 PMANGLE(del) (SET *table)
221 {
222 #ifdef DEBUG
223   MANGLEP(tag) = table->tag;
224 #endif
225   obstack_free (&table->obst, NULL);
226   xfree (table);
227 }
228
229 int
230 MANGLEP(count) (SET *table)
231 {
232   return table->nkey;
233 }
234
235 /*
236  * do one iteration step, return 1
237  * if still data in the set, 0 else
238  */
239 static INLINE int
240 iter_step (SET *table)
241 {
242   if (++table->iter_j >= SEGMENT_SIZE) {
243     table->iter_j = 0;
244     if (++table->iter_i >= table->nseg) {
245       table->iter_i = 0;
246       return 0;
247     }
248   }
249   return 1;
250 }
251
252 /*
253  * finds the first entry in the table
254  */
255 void *
256 MANGLEP(first) (SET *table)
257 {
258   assert (!table->iter_tail);
259   table->iter_i = 0;
260   table->iter_j = 0;
261   while (!table->dir[table->iter_i][table->iter_j]) {
262     if (!iter_step (table)) return NULL;
263   }
264   table->iter_tail = table->dir[table->iter_i][table->iter_j];
265   assert (table->iter_tail->entry.dptr);
266   return table->iter_tail->entry.dptr;
267 }
268
269 /*
270  * returns next entry in the table
271  */
272 void *
273 MANGLEP(next) (SET *table)
274 {
275   if (!table->iter_tail)
276     return NULL;
277
278   /* follow collision chain */
279   table->iter_tail = table->iter_tail->chain;
280   if (!table->iter_tail) {
281     /* go to next segment */
282     do {
283       if (!iter_step (table)) return NULL;
284     } while (!table->dir[table->iter_i][table->iter_j]);
285     table->iter_tail = table->dir[table->iter_i][table->iter_j];
286   }
287   assert (table->iter_tail->entry.dptr);
288   return table->iter_tail->entry.dptr;
289 }
290
291 void
292 MANGLEP(break) (SET *table)
293 {
294   table->iter_tail = NULL;
295 }
296
297 /*
298  * limit the hash value
299  */
300 static INLINE unsigned
301 Hash (SET *table, unsigned h)
302 {
303   unsigned address;
304
305   address = h & (table->maxp - 1);          /* h % table->maxp */
306   if (address < (unsigned)table->p)
307     address = h & ((table->maxp << 1) - 1); /* h % (2*table->maxp) */
308   return address;
309 }
310
311 /*
312  * returns non-zero if the number of elements in
313  * the set is greater then number of segments * MAX_LOAD_FACTOR
314  */
315 static INLINE int
316 loaded (SET *table)
317 {
318   return (  ++table->nkey
319           > (table->nseg << SEGMENT_SIZE_SHIFT) * MAX_LOAD_FACTOR);
320 }
321
322 /*
323  * expand the hash-table: the algorithm is split, so on every
324  * insert, only ONE segment is rehashed!
325  *
326  * table->p contains the current segment to split
327  * after all segments were split, table->p is set to zero and
328  * table->maxp is duplicated.
329  */
330 static void
331 expand_table (SET *table)
332 {
333   unsigned NewAddress;
334   int OldSegmentIndex, NewSegmentIndex;
335   int OldSegmentDir, NewSegmentDir;
336   Segment *OldSegment;
337   Segment *NewSegment;
338   Element *Current;
339   Element **Previous;
340   Element **LastOfNew;
341
342   if (table->maxp + table->p < (DIRECTORY_SIZE << SEGMENT_SIZE_SHIFT)) {
343     /* Locate the bucket to be split */
344     OldSegmentDir   = table->p >> SEGMENT_SIZE_SHIFT;
345     OldSegment      = table->dir[OldSegmentDir];
346     OldSegmentIndex = table->p & (SEGMENT_SIZE-1);
347
348     /* Expand address space; if necessary create a new segment */
349     NewAddress      = table->maxp + table->p;
350     NewSegmentDir   = NewAddress >> SEGMENT_SIZE_SHIFT;
351     NewSegmentIndex = NewAddress & (SEGMENT_SIZE-1);
352     if (NewSegmentIndex == 0) {
353       table->dir[NewSegmentDir] =
354         (Segment *)obstack_alloc (&table->obst,
355                                   sizeof(Segment) * SEGMENT_SIZE);
356       memset(table->dir[NewSegmentDir], 0, sizeof(Segment) * SEGMENT_SIZE);
357       table->nseg++;
358     }
359     NewSegment = table->dir[NewSegmentDir];
360
361     /* Adjust state variables */
362     table->p++;
363     if (table->p == table->maxp) {
364       table->maxp <<= 1;        /* table->maxp *= 2     */
365       table->p = 0;
366     }
367
368     /* Relocate records to the new bucket */
369     Previous = &OldSegment[OldSegmentIndex];
370     Current = *Previous;
371     LastOfNew = &NewSegment[NewSegmentIndex];
372     *LastOfNew = NULL;
373     while (Current != NULL) {
374       if (Hash (table, Current->entry.hash) == NewAddress) {
375         /* move to new chain */
376         *LastOfNew = Current;
377         *Previous  = Current->chain;
378         LastOfNew  = &Current->chain;
379         Current    = Current->chain;
380         *LastOfNew = NULL;
381       } else {
382         /* leave on old chain */
383         Previous = &Current->chain;
384         Current = Current->chain;
385       }
386     }
387   }
388 }
389
390
391 void *
392 MANGLE(_,_search) (SET *table,
393                    const void *key,
394 #ifndef PSET
395                    size_t size,
396 #endif
397                    unsigned hash,
398                    MANGLE(_,_action) action)
399 {
400   unsigned h;
401   Segment *CurrentSegment;
402   int SegmentIndex;
403   MANGLEP(cmp_fun) cmp = table->cmp;
404   Segment q;
405   int chain_len = 0;
406
407   assert (table);
408   assert (!table->iter_tail);
409   assert (key);
410 #ifdef DEBUG
411   MANGLEP(tag) = table->tag;
412 #endif
413   stat_access (table);
414
415   /* Find collision chain */
416   h = Hash (table, hash);
417   SegmentIndex   = h & (SEGMENT_SIZE-1);
418   CurrentSegment = table->dir[h >> SEGMENT_SIZE_SHIFT];
419   assert (CurrentSegment != NULL);
420   q = CurrentSegment[SegmentIndex];
421
422   /* Follow collision chain */
423   while (q && !EQUAL (cmp, q, key, size)) {
424     q = q->chain;
425     ++chain_len;
426   }
427
428   stat_chain_len (table, chain_len);
429
430   if (!q && (action != MANGLE(_,_find))) { /* not found, insert */
431     if (CurrentSegment[SegmentIndex]) stat_dup (table);
432
433 #ifdef PSET
434     if (table->free_list) {
435       q = table->free_list;
436       table->free_list = table->free_list->chain;
437     } else {
438       q = obstack_alloc (&table->obst, sizeof (Element));
439     }
440     q->entry.dptr = (void *)key;
441 #else
442     obstack_blank (&table->obst, offsetof (Element, entry.dptr));
443     if (action == _set_hinsert0)
444       obstack_grow0 (&table->obst, key, size);
445     else
446       obstack_grow (&table->obst, key, size);
447     q = obstack_finish (&table->obst);
448     q->entry.size = size;
449 #endif
450     q->chain = CurrentSegment[SegmentIndex];
451     q->entry.hash = hash;
452     CurrentSegment[SegmentIndex] = q;
453
454     if (loaded (table)) {
455       expand_table(table);      /* doesn't affect q */
456     }
457   }
458
459   if (!q) return NULL;
460 #ifdef PSET
461   if (action == _pset_hinsert) return &q->entry;
462 #else
463   if (action == _set_hinsert || action == _set_hinsert0) return &q->entry;
464 #endif
465   return q->entry.dptr;
466 }
467
468
469 #ifdef PSET
470
471 void *
472 pset_remove (SET *table, const void *key, unsigned hash)
473 {
474   unsigned h;
475   Segment *CurrentSegment;
476   int SegmentIndex;
477   pset_cmp_fun cmp = table->cmp;
478   Segment *p;
479   Segment q;
480   int chain_len = 0;
481
482   assert (table && !table->iter_tail);
483   stat_access (table);
484
485   /* Find collision chain */
486   h = Hash (table, hash);
487   SegmentIndex = h & (SEGMENT_SIZE-1);
488   CurrentSegment = table->dir[h >> SEGMENT_SIZE_SHIFT];
489   assert (CurrentSegment != NULL);
490   p = &CurrentSegment[SegmentIndex];
491
492   /* Follow collision chain */
493   while (!EQUAL (cmp, *p, key, size)) {
494     p = &(*p)->chain;
495     assert (*p);
496     ++chain_len;
497   }
498
499   stat_chain_len (table, chain_len);
500
501   q = *p;
502
503   if (q == table->iter_tail) {
504     /* removing current element */
505     table->iter_tail = q->chain;
506     if (!table->iter_tail) {
507       /* go to next segment */
508       do {
509         if (!iter_step (table))
510           break;
511       } while (!table->dir[table->iter_i][table->iter_j]);
512       table->iter_tail = table->dir[table->iter_i][table->iter_j];
513     }
514   }
515
516   *p = (*p)->chain;
517   q->chain = table->free_list;
518   table->free_list = q;
519   --table->nkey;
520
521   return q->entry.dptr;
522 }
523
524
525 void *
526 (pset_find) (SET *se, const void *key, unsigned hash)
527 {
528   return pset_find (se, key, hash);
529 }
530
531
532 void *
533 (pset_insert) (SET *se, const void *key, unsigned hash)
534 {
535   return pset_insert (se, key, hash);
536 }
537
538
539 MANGLEP(entry) *
540 (pset_hinsert) (SET *se, const void *key, unsigned hash)
541 {
542   return pset_hinsert (se, key, hash);
543 }
544
545 #else /* !PSET */
546
547 void *
548 (set_find) (set *se, const void *key, size_t size, unsigned hash)
549 {
550   return set_find (se, key, size, hash);
551 }
552
553
554 void *
555 (set_insert) (set *se, const void *key, size_t size, unsigned hash)
556 {
557   return set_insert (se, key, size, hash);
558 }
559
560
561 set_entry *
562 (set_hinsert) (set *se, const void *key, size_t size, unsigned hash)
563 {
564   return set_hinsert (se, key, size, hash);
565 }
566
567 #endif /* !PSET */