Remove .*_ptr variants of firm-types
[libfirm] / ir / adt / set.c
1 /*
2  * Copyright (C) 1995-2011 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  */
25
26 /*  This code is derived from:
27
28     From: ejp@ausmelb.oz.AU (Esmond Pitt)
29     Date: Tue, 7 Mar 1989 22:06:26 GMT
30     Subject: v06i042: dynamic hashing version of hsearch(3)
31     Message-ID: <1821@basser.oz>
32     Newsgroups: comp.sources.misc
33     Sender: msgs@basser.oz
34
35     Posting-number: Volume 6, Issue 42
36     Submitted-By: Esmond Pitt <ejp@ausmelb.oz.AU>
37     Archive-name: dynamic-hash
38
39     * Dynamic hashing, after CACM April 1988 pp 446-457, by Per-Ake Larson.
40     * Coded into C, with minor code improvements, and with hsearch(3) interface,
41     * by ejp@ausmelb.oz, Jul 26, 1988: 13:16;
42
43     TODO: Fix Esmond's ugly MixedCapsIdentifiers ;->
44  */
45 #include "config.h"
46
47 #ifdef PSET
48 # define SET pset
49 # define PMANGLE(pre) pre##_pset
50 # define MANGLEP(post) pset_##post
51 # define MANGLE(pre, post) pre##pset##post
52 # define EQUAL(cmp, elt, key, siz) (!(cmp) ((elt)->entry.dptr, (key)))
53 #else
54 # define SET set
55 # define PMANGLE(pre) pre##_set
56 # define MANGLEP(post) set_##post
57 # define MANGLE(pre, post) pre##set##post
58 # define EQUAL(cmp, elt, key, siz) \
59     (((elt)->entry.size == (siz)) && !(cmp) ((elt)->entry.dptr, (key), (siz)))
60 #endif
61
62 #include <assert.h>
63 #include <stdlib.h>
64 #include <stdio.h>
65 #include <string.h>
66 #include "xmalloc.h"
67 #include "lc_printf.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         size_t p;             /**< Next bucket to be split */
94         size_t maxp;          /**< upper bound on p during expansion */
95         size_t nkey;          /**< current # keys */
96         size_t 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         size_t naccess, ncollision, ndups;
107         size_t max_chain_len;
108 #endif
109 };
110
111
112 #ifdef STATS
113
114 void MANGLEP(stats) (SET *table)
115 {
116         size_t nfree = 0;
117 #ifdef PSET
118         Element *q = table->free_list;
119         while (q) { q = q->chain; ++nfree; }
120 #endif
121         lc_printf("     accesses  collisions        keys  duplicates     longest      wasted\n%12zu%12zu%12zu%12zu%12zu%12zu\n",
122                         table->naccess, table->ncollision, table->nkey, table->ndups, table->max_chain_len, nfree);
123 }
124
125 static inline void stat_chain_len(SET *table, size_t 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)chain_len)
137 # define stat_access(table) ((void)0)
138 # define stat_dup(table) ((void)0)
139
140 #endif /* !STATS */
141
142 SET *(PMANGLE(new)) (MANGLEP(cmp_fun) cmp, size_t nslots)
143 {
144         SET   *table = XMALLOC(SET);
145         size_t i;
146
147         if (nslots > SEGMENT_SIZE * DIRECTORY_SIZE)
148                 nslots = DIRECTORY_SIZE;
149         else {
150                 /* Adjust nslots up to next power of 2, minimum SEGMENT_SIZE */
151                 for (i = SEGMENT_SIZE;  i < nslots;  i <<= 1) {
152                 }
153                 nslots = i >> SEGMENT_SIZE_SHIFT;
154         }
155
156         table->nseg = table->p = table->nkey = 0;
157         table->maxp = nslots << SEGMENT_SIZE_SHIFT;
158         table->cmp = cmp;
159         table->iter_tail = NULL;
160 #ifdef PSET
161         table->free_list = NULL;
162 #endif
163         obstack_init (&table->obst);
164
165         /* Make segments */
166         for (i = 0;  i < nslots;  ++i) {
167                 table->dir[i] = OALLOCNZ(&table->obst, Segment, SEGMENT_SIZE);
168                 table->nseg++;
169         }
170
171 #ifdef STATS
172         table->naccess = table->ncollision = table->ndups = 0;
173         table->max_chain_len = 0;
174 #endif
175         return table;
176 }
177
178
179 void PMANGLE(del) (SET *table)
180 {
181         obstack_free (&table->obst, NULL);
182         xfree (table);
183 }
184
185 size_t MANGLEP(count) (SET *table)
186 {
187         return table->nkey;
188 }
189
190 /*
191  * do one iteration step, return 1
192  * if still data in the set, 0 else
193  */
194 static inline int iter_step(SET *table)
195 {
196         if (++table->iter_j >= SEGMENT_SIZE) {
197                 table->iter_j = 0;
198                 if (++table->iter_i >= table->nseg) {
199                         table->iter_i = 0;
200                         return 0;
201                 }
202         }
203         return 1;
204 }
205
206 /*
207  * finds the first entry in the table
208  */
209 void * MANGLEP(first) (SET *table)
210 {
211         assert (!table->iter_tail);
212         table->iter_i = 0;
213         table->iter_j = 0;
214         while (!table->dir[table->iter_i][table->iter_j]) {
215                 if (!iter_step (table)) return NULL;
216         }
217         table->iter_tail = table->dir[table->iter_i][table->iter_j];
218         assert (table->iter_tail->entry.dptr);
219         return table->iter_tail->entry.dptr;
220 }
221
222 /*
223  * returns next entry in the table
224  */
225 void *MANGLEP(next) (SET *table)
226 {
227         if (!table->iter_tail)
228                 return NULL;
229
230         /* follow collision chain */
231         table->iter_tail = table->iter_tail->chain;
232         if (!table->iter_tail) {
233                 /* go to next segment */
234                 do {
235                         if (!iter_step (table)) return NULL;
236                 } while (!table->dir[table->iter_i][table->iter_j]);
237                 table->iter_tail = table->dir[table->iter_i][table->iter_j];
238         }
239         assert (table->iter_tail->entry.dptr);
240         return table->iter_tail->entry.dptr;
241 }
242
243 void MANGLEP(break) (SET *table)
244 {
245         table->iter_tail = NULL;
246 }
247
248 /*
249  * limit the hash value
250  */
251 static inline unsigned Hash(SET *table, unsigned h)
252 {
253         unsigned address;
254         address = h & (table->maxp - 1);          /* h % table->maxp */
255         if (address < (unsigned)table->p)
256                 address = h & ((table->maxp << 1) - 1); /* h % (2*table->maxp) */
257         return address;
258 }
259
260 /*
261  * returns non-zero if the number of elements in
262  * the set is greater then number of segments * MAX_LOAD_FACTOR
263  */
264 static inline int loaded(SET *table)
265 {
266         return (  ++table->nkey
267                         > (table->nseg << SEGMENT_SIZE_SHIFT) * MAX_LOAD_FACTOR);
268 }
269
270 /*
271  * expand the hash-table: the algorithm is split, so on every
272  * insert, only ONE segment is rehashed!
273  *
274  * table->p contains the current segment to split
275  * after all segments were split, table->p is set to zero and
276  * table->maxp is duplicated.
277  */
278 static void expand_table(SET *table)
279 {
280         size_t NewAddress;
281         size_t OldSegmentIndex, NewSegmentIndex;
282         size_t OldSegmentDir, NewSegmentDir;
283         Segment *OldSegment;
284         Segment *NewSegment;
285         Element *Current;
286         Element **Previous;
287         Element **LastOfNew;
288
289         if (table->maxp + table->p < (DIRECTORY_SIZE << SEGMENT_SIZE_SHIFT)) {
290                 /* Locate the bucket to be split */
291                 OldSegmentDir   = table->p >> SEGMENT_SIZE_SHIFT;
292                 OldSegment      = table->dir[OldSegmentDir];
293                 OldSegmentIndex = table->p & (SEGMENT_SIZE-1);
294
295                 /* Expand address space; if necessary create a new segment */
296                 NewAddress      = table->maxp + table->p;
297                 NewSegmentDir   = NewAddress >> SEGMENT_SIZE_SHIFT;
298                 NewSegmentIndex = NewAddress & (SEGMENT_SIZE-1);
299                 if (NewSegmentIndex == 0) {
300                         table->dir[NewSegmentDir] = OALLOCNZ(&table->obst, Segment, SEGMENT_SIZE);
301                         table->nseg++;
302                 }
303                 NewSegment = table->dir[NewSegmentDir];
304
305                 /* Adjust state variables */
306                 table->p++;
307                 if (table->p == table->maxp) {
308                         table->maxp <<= 1;  /* table->maxp *= 2 */
309                         table->p = 0;
310                 }
311
312                 /* Relocate records to the new bucket */
313                 Previous = &OldSegment[OldSegmentIndex];
314                 Current = *Previous;
315                 LastOfNew = &NewSegment[NewSegmentIndex];
316                 *LastOfNew = NULL;
317                 while (Current != NULL) {
318                         if (Hash (table, Current->entry.hash) == NewAddress) {
319                                 /* move to new chain */
320                                 *LastOfNew = Current;
321                                 *Previous  = Current->chain;
322                                 LastOfNew  = &Current->chain;
323                                 Current    = Current->chain;
324                                 *LastOfNew = NULL;
325                         } else {
326                                 /* leave on old chain */
327                                 Previous = &Current->chain;
328                                 Current = Current->chain;
329                         }
330                 }
331         }
332 }
333
334
335 void * MANGLE(_,_search) (SET *table,
336                 const void *key,
337 #ifndef PSET
338                 size_t size,
339 #endif
340                 unsigned hash,
341                 MANGLE(_,_action) action)
342 {
343         unsigned h;
344         Segment *CurrentSegment;
345         int SegmentIndex;
346         MANGLEP(cmp_fun) cmp = table->cmp;
347         Segment q;
348         size_t chain_len = 0;
349
350         assert (table);
351         assert (key);
352         stat_access (table);
353
354         /* Find collision chain */
355         h = Hash (table, hash);
356         SegmentIndex   = h & (SEGMENT_SIZE-1);
357         CurrentSegment = table->dir[h >> SEGMENT_SIZE_SHIFT];
358         assert (CurrentSegment != NULL);
359         q = CurrentSegment[SegmentIndex];
360
361         /* Follow collision chain */
362         while (q && !EQUAL (cmp, q, key, size)) {
363                 q = q->chain;
364                 ++chain_len;
365         }
366
367         stat_chain_len(table, chain_len);
368
369         if (!q && (action != MANGLE(_,_find))) { /* not found, insert */
370                 assert (!table->iter_tail && "insert an element into a set that is iterated");
371
372                 if (CurrentSegment[SegmentIndex]) stat_dup (table);
373
374 #ifdef PSET
375                 if (table->free_list) {
376                         q = table->free_list;
377                         table->free_list = table->free_list->chain;
378                 } else {
379                         q = OALLOC(&table->obst, Element);
380                 }
381                 q->entry.dptr = (void *)key;
382 #else
383                 obstack_blank (&table->obst, offsetof (Element, entry.dptr));
384                 if (action == _set_hinsert0)
385                         obstack_grow0 (&table->obst, key, size);
386                 else
387                         obstack_grow (&table->obst, key, size);
388                 q = (Segment) obstack_finish (&table->obst);
389                 q->entry.size = size;
390 #endif
391                 q->chain = CurrentSegment[SegmentIndex];
392                 q->entry.hash = hash;
393                 CurrentSegment[SegmentIndex] = q;
394
395                 if (loaded (table)) {
396                         expand_table(table);    /* doesn't affect q */
397                 }
398         }
399
400         if (!q) return NULL;
401 #ifdef PSET
402         if (action == _pset_hinsert) return &q->entry;
403 #else
404         if (action == _set_hinsert || action == _set_hinsert0) return &q->entry;
405 #endif
406         return q->entry.dptr;
407 }
408
409
410 #ifdef PSET
411
412 int pset_default_ptr_cmp(const void *x, const void *y)
413 {
414         return x != y;
415 }
416
417 void *pset_remove(SET *table, const void *key, unsigned hash)
418 {
419         unsigned h;
420         Segment *CurrentSegment;
421         int SegmentIndex;
422         pset_cmp_fun cmp = table->cmp;
423         Segment *p;
424         Segment q;
425         int chain_len = 0;
426
427         assert (table && !table->iter_tail);
428         stat_access (table);
429
430         /* Find collision chain */
431         h = Hash (table, hash);
432         SegmentIndex = h & (SEGMENT_SIZE-1);
433         CurrentSegment = table->dir[h >> SEGMENT_SIZE_SHIFT];
434         assert (CurrentSegment != NULL);
435         p = &CurrentSegment[SegmentIndex];
436
437         /* Follow collision chain */
438         while (!EQUAL (cmp, *p, key, size)) {
439                 p = &(*p)->chain;
440                 assert (*p);
441                 ++chain_len;
442         }
443
444         stat_chain_len (table, chain_len);
445
446         q = *p;
447
448         if (q == table->iter_tail) {
449                 /* removing current element */
450                 table->iter_tail = q->chain;
451                 if (!table->iter_tail) {
452                         /* go to next segment */
453                         do {
454                                 if (!iter_step (table))
455                                         break;
456                         } while (!table->dir[table->iter_i][table->iter_j]);
457                         table->iter_tail = table->dir[table->iter_i][table->iter_j];
458                 }
459         }
460
461         *p = (*p)->chain;
462         q->chain = table->free_list;
463         table->free_list = q;
464         --table->nkey;
465
466         return q->entry.dptr;
467 }
468
469
470 void *(pset_find) (SET *se, const void *key, unsigned hash)
471 {
472         return pset_find (se, key, hash);
473 }
474
475
476 void *(pset_insert) (SET *se, const void *key, unsigned hash)
477 {
478         return pset_insert (se, key, hash);
479 }
480
481
482         MANGLEP(entry) *
483 (pset_hinsert) (SET *se, const void *key, unsigned hash)
484 {
485         return pset_hinsert (se, key, hash);
486 }
487
488 void pset_insert_pset_ptr(pset *target, pset *src)
489 {
490         void *elt;
491         for (elt = pset_first(src); elt; elt = pset_next(src)) {
492                 pset_insert_ptr(target, elt);
493         }
494 }
495
496 #else /* !PSET */
497
498 void *(set_find) (set *se, const void *key, size_t size, unsigned hash)
499 {
500         return set_find (se, key, size, hash);
501 }
502
503
504 void *(set_insert) (set *se, const void *key, size_t size, unsigned hash)
505 {
506         return set_insert (se, key, size, hash);
507 }
508
509
510 set_entry *(set_hinsert) (set *se, const void *key, size_t size, unsigned hash)
511 {
512         return set_hinsert (se, key, size, hash);
513 }
514
515 #endif /* !PSET */