2 * Copyright (C) 1995-2011 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
26 /* This code is derived from:
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
35 Posting-number: Volume 6, Issue 42
36 Submitted-By: Esmond Pitt <ejp@ausmelb.oz.AU>
37 Archive-name: dynamic-hash
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;
43 TODO: Fix Esmond's ugly MixedCapsIdentifiers ;->
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)))
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)))
67 #include "lc_printf.h"
75 #define TOBSTACK_ID MANGLEP(tag)
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
86 typedef struct element {
87 struct element *chain; /**< for chaining Elements */
88 MANGLEP (entry) entry;
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 */
102 Element *free_list; /**< list of free Elements */
104 struct obstack obst; /**< obstack for allocation all data */
106 size_t naccess, ncollision, ndups;
107 size_t max_chain_len;
114 void MANGLEP(stats) (SET *table)
118 Element *q = table->free_list;
119 while (q) { q = q->chain; ++nfree; }
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);
125 static inline void stat_chain_len(SET *table, size_t chain_len)
127 table->ncollision += chain_len;
128 if (table->max_chain_len < chain_len) table->max_chain_len = chain_len;
131 # define stat_access(table) (++(table)->naccess)
132 # define stat_dup(table) (++(table)->ndups)
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)
142 SET *(PMANGLE(new)) (MANGLEP(cmp_fun) cmp, size_t nslots)
144 SET *table = XMALLOC(SET);
147 if (nslots > SEGMENT_SIZE * DIRECTORY_SIZE)
148 nslots = DIRECTORY_SIZE;
150 /* Adjust nslots up to next power of 2, minimum SEGMENT_SIZE */
151 for (i = SEGMENT_SIZE; i < nslots; i <<= 1) {
153 nslots = i >> SEGMENT_SIZE_SHIFT;
156 table->nseg = table->p = table->nkey = 0;
157 table->maxp = nslots << SEGMENT_SIZE_SHIFT;
159 table->iter_tail = NULL;
161 table->free_list = NULL;
163 obstack_init (&table->obst);
166 for (i = 0; i < nslots; ++i) {
167 table->dir[i] = OALLOCNZ(&table->obst, Segment, SEGMENT_SIZE);
172 table->naccess = table->ncollision = table->ndups = 0;
173 table->max_chain_len = 0;
179 void PMANGLE(del) (SET *table)
181 obstack_free (&table->obst, NULL);
185 size_t MANGLEP(count) (SET *table)
191 * do one iteration step, return 1
192 * if still data in the set, 0 else
194 static inline int iter_step(SET *table)
196 if (++table->iter_j >= SEGMENT_SIZE) {
198 if (++table->iter_i >= table->nseg) {
207 * finds the first entry in the table
209 void * MANGLEP(first) (SET *table)
211 assert (!table->iter_tail);
214 while (!table->dir[table->iter_i][table->iter_j]) {
215 if (!iter_step (table)) return NULL;
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;
223 * returns next entry in the table
225 void *MANGLEP(next) (SET *table)
227 if (!table->iter_tail)
230 /* follow collision chain */
231 table->iter_tail = table->iter_tail->chain;
232 if (!table->iter_tail) {
233 /* go to next segment */
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];
239 assert (table->iter_tail->entry.dptr);
240 return table->iter_tail->entry.dptr;
243 void MANGLEP(break) (SET *table)
245 table->iter_tail = NULL;
249 * limit the hash value
251 static inline unsigned Hash(SET *table, unsigned h)
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) */
261 * returns non-zero if the number of elements in
262 * the set is greater then number of segments * MAX_LOAD_FACTOR
264 static inline int loaded(SET *table)
266 return ( ++table->nkey
267 > (table->nseg << SEGMENT_SIZE_SHIFT) * MAX_LOAD_FACTOR);
271 * expand the hash-table: the algorithm is split, so on every
272 * insert, only ONE segment is rehashed!
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.
278 static void expand_table(SET *table)
281 size_t OldSegmentIndex, NewSegmentIndex;
282 size_t OldSegmentDir, NewSegmentDir;
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);
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);
303 NewSegment = table->dir[NewSegmentDir];
305 /* Adjust state variables */
307 if (table->p == table->maxp) {
308 table->maxp <<= 1; /* table->maxp *= 2 */
312 /* Relocate records to the new bucket */
313 Previous = &OldSegment[OldSegmentIndex];
315 LastOfNew = &NewSegment[NewSegmentIndex];
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;
326 /* leave on old chain */
327 Previous = &Current->chain;
328 Current = Current->chain;
335 void * MANGLE(_,_search) (SET *table,
341 MANGLE(_,_action) action)
344 Segment *CurrentSegment;
346 MANGLEP(cmp_fun) cmp = table->cmp;
348 size_t chain_len = 0;
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];
361 /* Follow collision chain */
362 while (q && !EQUAL (cmp, q, key, size)) {
367 stat_chain_len(table, chain_len);
369 if (!q && (action != MANGLE(_,_find))) { /* not found, insert */
370 assert (!table->iter_tail && "insert an element into a set that is iterated");
372 if (CurrentSegment[SegmentIndex]) stat_dup (table);
375 if (table->free_list) {
376 q = table->free_list;
377 table->free_list = table->free_list->chain;
379 q = OALLOC(&table->obst, Element);
381 q->entry.dptr = (void *)key;
383 obstack_blank (&table->obst, offsetof (Element, entry.dptr));
384 if (action == _set_hinsert0)
385 obstack_grow0 (&table->obst, key, size);
387 obstack_grow (&table->obst, key, size);
388 q = (Segment) obstack_finish (&table->obst);
389 q->entry.size = size;
391 q->chain = CurrentSegment[SegmentIndex];
392 q->entry.hash = hash;
393 CurrentSegment[SegmentIndex] = q;
395 if (loaded (table)) {
396 expand_table(table); /* doesn't affect q */
402 if (action == _pset_hinsert) return &q->entry;
404 if (action == _set_hinsert || action == _set_hinsert0) return &q->entry;
406 return q->entry.dptr;
412 int pset_default_ptr_cmp(const void *x, const void *y)
417 void *pset_remove(SET *table, const void *key, unsigned hash)
420 Segment *CurrentSegment;
422 pset_cmp_fun cmp = table->cmp;
427 assert (table && !table->iter_tail);
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];
437 /* Follow collision chain */
438 while (!EQUAL (cmp, *p, key, size)) {
444 stat_chain_len (table, chain_len);
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 */
454 if (!iter_step (table))
456 } while (!table->dir[table->iter_i][table->iter_j]);
457 table->iter_tail = table->dir[table->iter_i][table->iter_j];
462 q->chain = table->free_list;
463 table->free_list = q;
466 return q->entry.dptr;
470 void *(pset_find) (SET *se, const void *key, unsigned hash)
472 return pset_find (se, key, hash);
476 void *(pset_insert) (SET *se, const void *key, unsigned hash)
478 return pset_insert (se, key, hash);
483 (pset_hinsert) (SET *se, const void *key, unsigned hash)
485 return pset_hinsert (se, key, hash);
488 void pset_insert_pset_ptr(pset *target, pset *src)
491 for (elt = pset_first(src); elt; elt = pset_next(src)) {
492 pset_insert_ptr(target, elt);
498 void *(set_find) (set *se, const void *key, size_t size, unsigned hash)
500 return set_find (se, key, size, hash);
504 void *(set_insert) (set *se, const void *key, size_t size, unsigned hash)
506 return set_insert (se, key, size, hash);
510 set_entry *(set_hinsert) (set *se, const void *key, size_t size, unsigned hash)
512 return set_hinsert (se, key, size, hash);