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