2 * Copyright (C) 1995-2007 University of Karlsruhe. All right reserved.
4 * This file is part of libFirm.
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.
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.
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
22 * @brief implementation of set
23 * @author Markus Armbruster
27 /* This code is derived from:
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
36 Posting-number: Volume 6, Issue 42
37 Submitted-By: Esmond Pitt <ejp@ausmelb.oz.AU>
38 Archive-name: dynamic-hash
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;
44 TODO: Fix Esmond's ugly MixedCapsIdentifiers ;->
50 #include "firm_config.h"
54 # define PMANGLE(pre) pre##_pset
55 # define MANGLEP(post) pset_##post
56 # define MANGLE(pre, post) pre##pset##post
57 # define EQUAL(cmp, elt, key, siz) (!(cmp) ((elt)->entry.dptr, (key)))
60 # define PMANGLE(pre) pre##_set
61 # define MANGLEP(post) set_##post
62 # define MANGLE(pre, post) pre##set##post
63 # define EQUAL(cmp, elt, key, siz) \
64 (((elt)->entry.size == (siz)) && !(cmp) ((elt)->entry.dptr, (key), (siz)))
79 #define TOBSTACK_ID MANGLEP(tag)
83 #define SEGMENT_SIZE_SHIFT 8
84 #define SEGMENT_SIZE (1 << SEGMENT_SIZE_SHIFT)
85 #define DIRECTORY_SIZE_SHIFT 8
86 #define DIRECTORY_SIZE (1 << DIRECTORY_SIZE_SHIFT)
87 #define MAX_LOAD_FACTOR 4
90 typedef struct element {
91 struct element *chain; /**< for chaining Elements */
92 MANGLEP (entry) entry;
97 unsigned p; /**< Next bucket to be split */
98 unsigned maxp; /**< upper bound on p during expansion */
99 unsigned nkey; /**< current # keys */
100 unsigned nseg; /**< current # segments */
101 Segment *dir[DIRECTORY_SIZE];
102 MANGLEP(cmp_fun) cmp; /**< function comparing entries */
103 unsigned iter_i, iter_j;
104 Element *iter_tail; /**< non-NULL while iterating over elts */
106 Element *free_list; /**< list of free Elements */
108 struct obstack obst; /**< obstack for allocation all data */
110 int naccess, ncollision, ndups;
114 const char *tag; /**< an optionally tag for distinguishing sets */
122 MANGLEP(stats) (SET *table)
126 Element *q = table->free_list;
127 while (q) { q = q->chain; ++nfree; }
129 printf (" accesses collisions keys duplicates longest wasted\n%12d%12d%12d%12d%12d%12d\n",
130 table->naccess, table->ncollision, table->nkey, table->ndups, table->max_chain_len, nfree);
134 stat_chain_len (SET *table, int chain_len)
136 table->ncollision += chain_len;
137 if (table->max_chain_len < chain_len) table->max_chain_len = chain_len;
140 # define stat_access(table) (++(table)->naccess)
141 # define stat_dup(table) (++(table)->ndups)
145 # define stat_chain_len(table, chain_len) ((void)0)
146 # define stat_access(table) ((void)0)
147 # define stat_dup(table) ((void)0)
153 const char *MANGLEP(tag);
157 MANGLEP(describe) (SET *table)
159 unsigned i, j, collide;
163 printf ("p=%u maxp=%u nkey=%u nseg=%u\n",
164 table->p, table->maxp, table->nkey, table->nseg);
165 for (i = 0; i < table->nseg; i++) {
167 for (j = 0; j < SEGMENT_SIZE; j++) {
171 if (collide) printf ("<%3d>", collide);
172 else printf ("table");
173 printf ("[%d][%3d]: %u %p\n", i, j, ptr->entry.hash, (void *)ptr->entry.dptr);
180 MANGLEP(stats)(table);
188 (PMANGLE(new)) (MANGLEP(cmp_fun) cmp, int nslots)
191 SET *table = xmalloc(sizeof(*table));
193 if (nslots > SEGMENT_SIZE * DIRECTORY_SIZE)
194 nslots = DIRECTORY_SIZE;
196 assert (nslots >= 0);
197 /* Adjust nslots up to next power of 2, minimum SEGMENT_SIZE */
198 for (i = SEGMENT_SIZE; i < nslots; i <<= 1);
199 nslots = i >> SEGMENT_SIZE_SHIFT;
202 table->nseg = table->p = table->nkey = 0;
203 table->maxp = nslots << SEGMENT_SIZE_SHIFT;
205 table->iter_tail = NULL;
207 table->free_list = NULL;
209 obstack_init (&table->obst);
212 for (i = 0; i < nslots; ++i) {
213 table->dir[i] = (Segment *)obstack_alloc (&table->obst,
214 sizeof (Segment) * SEGMENT_SIZE);
216 memset(table->dir[i], 0, sizeof (Segment) * SEGMENT_SIZE);
221 table->naccess = table->ncollision = table->ndups = 0;
222 table->max_chain_len = 0;
225 table->tag = MANGLEP(tag);
232 PMANGLE(del) (SET *table)
235 MANGLEP(tag) = table->tag;
237 obstack_free (&table->obst, NULL);
242 MANGLEP(count) (SET *table)
248 * do one iteration step, return 1
249 * if still data in the set, 0 else
252 iter_step (SET *table)
254 if (++table->iter_j >= SEGMENT_SIZE) {
256 if (++table->iter_i >= table->nseg) {
265 * finds the first entry in the table
268 MANGLEP(first) (SET *table)
270 assert (!table->iter_tail);
273 while (!table->dir[table->iter_i][table->iter_j]) {
274 if (!iter_step (table)) return NULL;
276 table->iter_tail = table->dir[table->iter_i][table->iter_j];
277 assert (table->iter_tail->entry.dptr);
278 return table->iter_tail->entry.dptr;
282 * returns next entry in the table
285 MANGLEP(next) (SET *table)
287 if (!table->iter_tail)
290 /* follow collision chain */
291 table->iter_tail = table->iter_tail->chain;
292 if (!table->iter_tail) {
293 /* go to next segment */
295 if (!iter_step (table)) return NULL;
296 } while (!table->dir[table->iter_i][table->iter_j]);
297 table->iter_tail = table->dir[table->iter_i][table->iter_j];
299 assert (table->iter_tail->entry.dptr);
300 return table->iter_tail->entry.dptr;
304 MANGLEP(break) (SET *table)
306 table->iter_tail = NULL;
310 * limit the hash value
312 static INLINE unsigned
313 Hash (SET *table, unsigned h)
316 address = h & (table->maxp - 1); /* h % table->maxp */
317 if (address < (unsigned)table->p)
318 address = h & ((table->maxp << 1) - 1); /* h % (2*table->maxp) */
323 * returns non-zero if the number of elements in
324 * the set is greater then number of segments * MAX_LOAD_FACTOR
329 return ( ++table->nkey
330 > (table->nseg << SEGMENT_SIZE_SHIFT) * MAX_LOAD_FACTOR);
334 * expand the hash-table: the algorithm is split, so on every
335 * insert, only ONE segment is rehashed!
337 * table->p contains the current segment to split
338 * after all segments were split, table->p is set to zero and
339 * table->maxp is duplicated.
342 expand_table (SET *table)
345 int OldSegmentIndex, NewSegmentIndex;
346 int OldSegmentDir, NewSegmentDir;
353 if (table->maxp + table->p < (DIRECTORY_SIZE << SEGMENT_SIZE_SHIFT)) {
354 /* Locate the bucket to be split */
355 OldSegmentDir = table->p >> SEGMENT_SIZE_SHIFT;
356 OldSegment = table->dir[OldSegmentDir];
357 OldSegmentIndex = table->p & (SEGMENT_SIZE-1);
359 /* Expand address space; if necessary create a new segment */
360 NewAddress = table->maxp + table->p;
361 NewSegmentDir = NewAddress >> SEGMENT_SIZE_SHIFT;
362 NewSegmentIndex = NewAddress & (SEGMENT_SIZE-1);
363 if (NewSegmentIndex == 0) {
364 table->dir[NewSegmentDir] =
365 (Segment *)obstack_alloc (&table->obst,
366 sizeof(Segment) * SEGMENT_SIZE);
367 memset(table->dir[NewSegmentDir], 0, sizeof(Segment) * SEGMENT_SIZE);
370 NewSegment = table->dir[NewSegmentDir];
372 /* Adjust state variables */
374 if (table->p == table->maxp) {
375 table->maxp <<= 1; /* table->maxp *= 2 */
379 /* Relocate records to the new bucket */
380 Previous = &OldSegment[OldSegmentIndex];
382 LastOfNew = &NewSegment[NewSegmentIndex];
384 while (Current != NULL) {
385 if (Hash (table, Current->entry.hash) == NewAddress) {
386 /* move to new chain */
387 *LastOfNew = Current;
388 *Previous = Current->chain;
389 LastOfNew = &Current->chain;
390 Current = Current->chain;
393 /* leave on old chain */
394 Previous = &Current->chain;
395 Current = Current->chain;
403 MANGLE(_,_search) (SET *table,
409 MANGLE(_,_action) action)
412 Segment *CurrentSegment;
414 MANGLEP(cmp_fun) cmp = table->cmp;
421 MANGLEP(tag) = table->tag;
425 /* Find collision chain */
426 h = Hash (table, hash);
427 SegmentIndex = h & (SEGMENT_SIZE-1);
428 CurrentSegment = table->dir[h >> SEGMENT_SIZE_SHIFT];
429 assert (CurrentSegment != NULL);
430 q = CurrentSegment[SegmentIndex];
432 /* Follow collision chain */
433 while (q && !EQUAL (cmp, q, key, size)) {
438 stat_chain_len (table, chain_len);
440 if (!q && (action != MANGLE(_,_find))) { /* not found, insert */
441 assert (!table->iter_tail && "insert an element into a set that is iterated");
443 if (CurrentSegment[SegmentIndex]) stat_dup (table);
446 if (table->free_list) {
447 q = table->free_list;
448 table->free_list = table->free_list->chain;
450 q = obstack_alloc (&table->obst, sizeof (Element));
452 q->entry.dptr = (void *)key;
454 obstack_blank (&table->obst, offsetof (Element, entry.dptr));
455 if (action == _set_hinsert0)
456 obstack_grow0 (&table->obst, key, size);
458 obstack_grow (&table->obst, key, size);
459 q = obstack_finish (&table->obst);
460 q->entry.size = size;
462 q->chain = CurrentSegment[SegmentIndex];
463 q->entry.hash = hash;
464 CurrentSegment[SegmentIndex] = q;
466 if (loaded (table)) {
467 expand_table(table); /* doesn't affect q */
473 if (action == _pset_hinsert) return &q->entry;
475 if (action == _set_hinsert || action == _set_hinsert0) return &q->entry;
477 return q->entry.dptr;
483 int pset_default_ptr_cmp(const void *x, const void *y)
489 pset_remove (SET *table, const void *key, unsigned hash)
492 Segment *CurrentSegment;
494 pset_cmp_fun cmp = table->cmp;
499 assert (table && !table->iter_tail);
502 /* Find collision chain */
503 h = Hash (table, hash);
504 SegmentIndex = h & (SEGMENT_SIZE-1);
505 CurrentSegment = table->dir[h >> SEGMENT_SIZE_SHIFT];
506 assert (CurrentSegment != NULL);
507 p = &CurrentSegment[SegmentIndex];
509 /* Follow collision chain */
510 while (!EQUAL (cmp, *p, key, size)) {
516 stat_chain_len (table, chain_len);
520 if (q == table->iter_tail) {
521 /* removing current element */
522 table->iter_tail = q->chain;
523 if (!table->iter_tail) {
524 /* go to next segment */
526 if (!iter_step (table))
528 } while (!table->dir[table->iter_i][table->iter_j]);
529 table->iter_tail = table->dir[table->iter_i][table->iter_j];
534 q->chain = table->free_list;
535 table->free_list = q;
538 return q->entry.dptr;
543 (pset_find) (SET *se, const void *key, unsigned hash)
545 return pset_find (se, key, hash);
550 (pset_insert) (SET *se, const void *key, unsigned hash)
552 return pset_insert (se, key, hash);
557 (pset_hinsert) (SET *se, const void *key, unsigned hash)
559 return pset_hinsert (se, key, hash);
562 void pset_insert_pset_ptr(pset *target, pset *src) {
564 for (elt = pset_first(src); elt; elt = pset_next(src)) {
565 pset_insert_ptr(target, elt);
572 (set_find) (set *se, const void *key, size_t size, unsigned hash)
574 return set_find (se, key, size, hash);
579 (set_insert) (set *se, const void *key, size_t size, unsigned hash)
581 return set_insert (se, key, size, hash);
586 (set_hinsert) (set *se, const void *key, size_t size, unsigned hash)
588 return set_hinsert (se, key, size, hash);