2 * This file is part of libFirm.
3 * Copyright (C) 2012 University of Karlsruhe.
8 * @brief implementation of set
9 * @author Markus Armbruster
12 /* This code is derived from:
14 From: ejp@ausmelb.oz.AU (Esmond Pitt)
15 Date: Tue, 7 Mar 1989 22:06:26 GMT
16 Subject: v06i042: dynamic hashing version of hsearch(3)
17 Message-ID: <1821@basser.oz>
18 Newsgroups: comp.sources.misc
19 Sender: msgs@basser.oz
21 Posting-number: Volume 6, Issue 42
22 Submitted-By: Esmond Pitt <ejp@ausmelb.oz.AU>
23 Archive-name: dynamic-hash
25 * Dynamic hashing, after CACM April 1988 pp 446-457, by Per-Ake Larson.
26 * Coded into C, with minor code improvements, and with hsearch(3) interface,
27 * by ejp@ausmelb.oz, Jul 26, 1988: 13:16;
29 TODO: Fix Esmond's ugly MixedCapsIdentifiers ;->
35 # define PMANGLE(pre) pre##_pset
36 # define MANGLEP(post) pset_##post
37 # define MANGLE(pre, post) pre##pset##post
38 # define EQUAL(cmp, elt, key, siz) (!(cmp) ((elt)->entry.dptr, (key)))
41 # define PMANGLE(pre) pre##_set
42 # define MANGLEP(post) set_##post
43 # define MANGLE(pre, post) pre##set##post
44 # define EQUAL(cmp, elt, key, siz) \
45 (((elt)->entry.size == (siz)) && !(cmp) ((elt)->entry.dptr, (key), (siz)))
53 #include "lc_printf.h"
61 #define TOBSTACK_ID MANGLEP(tag)
65 #define SEGMENT_SIZE_SHIFT 8
66 #define SEGMENT_SIZE (1 << SEGMENT_SIZE_SHIFT)
67 #define DIRECTORY_SIZE_SHIFT 8
68 #define DIRECTORY_SIZE (1 << DIRECTORY_SIZE_SHIFT)
69 #define MAX_LOAD_FACTOR 4
72 typedef struct element {
73 struct element *chain; /**< for chaining Elements */
74 MANGLEP (entry) entry;
79 size_t p; /**< Next bucket to be split */
80 size_t maxp; /**< upper bound on p during expansion */
81 size_t nkey; /**< current # keys */
82 size_t nseg; /**< current # segments */
83 Segment *dir[DIRECTORY_SIZE];
84 MANGLEP(cmp_fun) cmp; /**< function comparing entries */
85 unsigned iter_i, iter_j;
86 Element *iter_tail; /**< non-NULL while iterating over elts */
88 Element *free_list; /**< list of free Elements */
90 struct obstack obst; /**< obstack for allocation all data */
94 SET *(PMANGLE(new)) (MANGLEP(cmp_fun) cmp, size_t nslots)
96 SET *table = XMALLOC(SET);
99 if (nslots > SEGMENT_SIZE * DIRECTORY_SIZE)
100 nslots = DIRECTORY_SIZE;
102 /* Adjust nslots up to next power of 2, minimum SEGMENT_SIZE */
103 for (i = SEGMENT_SIZE; i < nslots; i <<= 1) {
105 nslots = i >> SEGMENT_SIZE_SHIFT;
108 table->nseg = table->p = table->nkey = 0;
109 table->maxp = nslots << SEGMENT_SIZE_SHIFT;
111 table->iter_tail = NULL;
113 table->free_list = NULL;
115 obstack_init (&table->obst);
118 for (i = 0; i < nslots; ++i) {
119 table->dir[i] = OALLOCNZ(&table->obst, Segment, SEGMENT_SIZE);
127 void PMANGLE(del) (SET *table)
129 obstack_free (&table->obst, NULL);
133 size_t MANGLEP(count) (SET *table)
139 * do one iteration step, return 1
140 * if still data in the set, 0 else
142 static inline int iter_step(SET *table)
144 if (++table->iter_j >= SEGMENT_SIZE) {
146 if (++table->iter_i >= table->nseg) {
155 * finds the first entry in the table
157 void *(MANGLEP(first))(SET *table)
159 assert (!table->iter_tail);
162 while (!table->dir[table->iter_i][table->iter_j]) {
163 if (!iter_step (table)) return NULL;
165 table->iter_tail = table->dir[table->iter_i][table->iter_j];
166 assert (table->iter_tail->entry.dptr);
167 return table->iter_tail->entry.dptr;
171 * returns next entry in the table
173 void *(MANGLEP(next))(SET *table)
175 if (!table->iter_tail)
178 /* follow collision chain */
179 table->iter_tail = table->iter_tail->chain;
180 if (!table->iter_tail) {
181 /* go to next segment */
183 if (!iter_step (table)) return NULL;
184 } while (!table->dir[table->iter_i][table->iter_j]);
185 table->iter_tail = table->dir[table->iter_i][table->iter_j];
187 assert (table->iter_tail->entry.dptr);
188 return table->iter_tail->entry.dptr;
191 void MANGLEP(break) (SET *table)
193 table->iter_tail = NULL;
197 * limit the hash value
199 static inline unsigned Hash(SET *table, unsigned h)
202 address = h & (table->maxp - 1); /* h % table->maxp */
203 if (address < (unsigned)table->p)
204 address = h & ((table->maxp << 1) - 1); /* h % (2*table->maxp) */
209 * returns non-zero if the number of elements in
210 * the set is greater then number of segments * MAX_LOAD_FACTOR
212 static inline int loaded(SET *table)
214 return ( ++table->nkey
215 > (table->nseg << SEGMENT_SIZE_SHIFT) * MAX_LOAD_FACTOR);
219 * expand the hash-table: the algorithm is split, so on every
220 * insert, only ONE segment is rehashed!
222 * table->p contains the current segment to split
223 * after all segments were split, table->p is set to zero and
224 * table->maxp is duplicated.
226 static void expand_table(SET *table)
229 size_t OldSegmentIndex, NewSegmentIndex;
230 size_t OldSegmentDir, NewSegmentDir;
237 if (table->maxp + table->p < (DIRECTORY_SIZE << SEGMENT_SIZE_SHIFT)) {
238 /* Locate the bucket to be split */
239 OldSegmentDir = table->p >> SEGMENT_SIZE_SHIFT;
240 OldSegment = table->dir[OldSegmentDir];
241 OldSegmentIndex = table->p & (SEGMENT_SIZE-1);
243 /* Expand address space; if necessary create a new segment */
244 NewAddress = table->maxp + table->p;
245 NewSegmentDir = NewAddress >> SEGMENT_SIZE_SHIFT;
246 NewSegmentIndex = NewAddress & (SEGMENT_SIZE-1);
247 if (NewSegmentIndex == 0) {
248 table->dir[NewSegmentDir] = OALLOCNZ(&table->obst, Segment, SEGMENT_SIZE);
251 NewSegment = table->dir[NewSegmentDir];
253 /* Adjust state variables */
255 if (table->p == table->maxp) {
256 table->maxp <<= 1; /* table->maxp *= 2 */
260 /* Relocate records to the new bucket */
261 Previous = &OldSegment[OldSegmentIndex];
263 LastOfNew = &NewSegment[NewSegmentIndex];
265 while (Current != NULL) {
266 if (Hash (table, Current->entry.hash) == NewAddress) {
267 /* move to new chain */
268 *LastOfNew = Current;
269 *Previous = Current->chain;
270 LastOfNew = &Current->chain;
271 Current = Current->chain;
274 /* leave on old chain */
275 Previous = &Current->chain;
276 Current = Current->chain;
283 void * MANGLE(_,_search) (SET *table,
289 MANGLE(_,_action) action)
292 Segment *CurrentSegment;
294 MANGLEP(cmp_fun) cmp = table->cmp;
300 /* Find collision chain */
301 h = Hash (table, hash);
302 SegmentIndex = h & (SEGMENT_SIZE-1);
303 CurrentSegment = table->dir[h >> SEGMENT_SIZE_SHIFT];
304 assert (CurrentSegment != NULL);
305 q = CurrentSegment[SegmentIndex];
307 /* Follow collision chain */
308 while (q && !EQUAL (cmp, q, key, size)) {
312 if (!q && (action != MANGLE(_,_find))) { /* not found, insert */
313 assert (!table->iter_tail && "insert an element into a set that is iterated");
316 if (table->free_list) {
317 q = table->free_list;
318 table->free_list = table->free_list->chain;
320 q = OALLOC(&table->obst, Element);
322 q->entry.dptr = (void *)key;
324 obstack_blank (&table->obst, offsetof (Element, entry.dptr));
325 if (action == _set_hinsert0)
326 obstack_grow0 (&table->obst, key, size);
328 obstack_grow (&table->obst, key, size);
329 q = (Segment) obstack_finish (&table->obst);
330 q->entry.size = size;
332 q->chain = CurrentSegment[SegmentIndex];
333 q->entry.hash = hash;
334 CurrentSegment[SegmentIndex] = q;
336 if (loaded (table)) {
337 expand_table(table); /* doesn't affect q */
343 if (action == _pset_hinsert) return &q->entry;
345 if (action == _set_hinsert || action == _set_hinsert0) return &q->entry;
347 return q->entry.dptr;
353 int pset_default_ptr_cmp(const void *x, const void *y)
358 void *pset_remove(SET *table, const void *key, unsigned hash)
361 Segment *CurrentSegment;
363 pset_cmp_fun cmp = table->cmp;
367 assert (table && !table->iter_tail);
369 /* Find collision chain */
370 h = Hash (table, hash);
371 SegmentIndex = h & (SEGMENT_SIZE-1);
372 CurrentSegment = table->dir[h >> SEGMENT_SIZE_SHIFT];
373 assert (CurrentSegment != NULL);
374 p = &CurrentSegment[SegmentIndex];
376 /* Follow collision chain */
377 while (!EQUAL (cmp, *p, key, size)) {
384 if (q == table->iter_tail) {
385 /* removing current element */
386 table->iter_tail = q->chain;
387 if (!table->iter_tail) {
388 /* go to next segment */
390 if (!iter_step (table))
392 } while (!table->dir[table->iter_i][table->iter_j]);
393 table->iter_tail = table->dir[table->iter_i][table->iter_j];
398 q->chain = table->free_list;
399 table->free_list = q;
402 return q->entry.dptr;
406 void *(pset_find) (SET *se, const void *key, unsigned hash)
408 return pset_find (se, key, hash);
412 void *(pset_insert) (SET *se, const void *key, unsigned hash)
414 return pset_insert (se, key, hash);
419 (pset_hinsert) (SET *se, const void *key, unsigned hash)
421 return pset_hinsert (se, key, hash);
424 void pset_insert_pset_ptr(pset *target, pset *src)
426 foreach_pset(src, void, elt) {
427 pset_insert_ptr(target, elt);
433 void *(set_find) (set *se, const void *key, size_t size, unsigned hash)
435 return set_find(void, se, key, size, hash);
439 void *(set_insert) (set *se, const void *key, size_t size, unsigned hash)
441 return set_insert(void, se, key, size, hash);
445 set_entry *(set_hinsert) (set *se, const void *key, size_t size, unsigned hash)
447 return set_hinsert (se, key, size, hash);