]> mj.ucw.cz Git - libucw.git/blob - lib/hashtable.h
Added very simple functions for emulating a fastbuf stream over a static
[libucw.git] / lib / hashtable.h
1 /*
2  *      Sherlock Library -- Universal Hash Table
3  *
4  *      (c) 2002 Martin Mares <mj@ucw.cz>
5  *      (c) 2002 Robert Spalek <robert@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 /*
12  *  This is not a normal header file, it's a generator of hash tables.
13  *  Each time you include it with parameters set in the corresponding
14  *  preprocessor macros, it generates a hash table with the parameters
15  *  given.
16  *
17  *  You need to specify:
18  *
19  *  HASH_NODE           data type where a node dwells (usually a struct).
20  *  HASH_PREFIX(x)      macro to add a name prefix (used on all global names
21  *                      defined by the hash table generator).
22  *
23  *  Then decide on type of keys:
24  *
25  *  HASH_KEY_ATOMIC=f   use node->f as a key of an atomic type (i.e.,
26  *                      a type which can be compared using `==')
27  *                      HASH_ATOMIC_TYPE (defaults to int).
28  *  | HASH_KEY_STRING=f use node->f as a string key, allocated
29  *                      separately from the rest of the node.
30  *  | HASH_KEY_ENDSTRING=f use node->f as a string key, allocated
31  *                      automatically at the end of the node struct
32  *                      (to be declared as "char f[1]" at the end).
33  *  | HASH_KEY_COMPLEX  use a multi-component key; as the name suggests,
34  *                      the passing of parameters is a bit complex then.
35  *                      The HASH_KEY_COMPLEX(x) macro should expand to
36  *                      `x k1, x k2, ... x kn' and you should also define:
37  *    HASH_KEY_DECL     declaration of function parameters in which key
38  *                      should be passed to all hash table operations.
39  *                      That is, `type1 k1, type2 k2, ... typen kn'.
40  *                      With complex keys, HASH_GIVE_HASHFN and HASH_GIVE_EQ
41  *                      are mandatory.
42  *
43  *  Then specify what operations you request (all names are automatically
44  *  prefixed by calling HASH_PREFIX):
45  *
46  *  <always defined>    init() -- initialize the hash table.
47  *  HASH_WANT_CLEANUP   cleanup() -- deallocate the hash table.
48  *  HASH_WANT_FIND      node *find(key) -- find first node with the specified
49  *                      key, return NULL if no such node exists.
50  *  HASH_WANT_FIND_NEXT node *find(node *start) -- find next node with the
51  *                      specified key, return NULL if no such node exists.
52  *  HASH_WANT_NEW       node *new(key) -- create new node with given key.
53  *                      Doesn't check whether it already exists.
54  *  HASH_WANT_LOOKUP    node *lookup(key) -- find node with given key,
55  *                      if it doesn't exist, create it. Defining
56  *                      HASH_GIVE_INIT_DATA is strongly recommended.
57  *  HASH_WANT_DELETE    int delete(key) -- delete and deallocate node
58  *                      with given key. Returns success.
59  *  HASH_WANT_REMOVE    remove(node *) -- delete and deallocate given node.
60  *
61  *  You can also supply several functions:
62  *
63  *  HASH_GIVE_HASHFN    unsigned int hash(key) -- calculate hash value of key.
64  *                      We have sensible default hash functions for strings
65  *                      and integers.
66  *  HASH_GIVE_EQ        int eq(key1, key2) -- return whether keys are equal.
67  *                      By default, we use == for atomic types and either
68  *                      strcmp or strcasecmp for strings.
69  *  HASH_GIVE_EXTRA_SIZE int extra_size(key) -- returns how many bytes after the
70  *                      node should be allocated for dynamic data. Default=0
71  *                      or length of the string with HASH_KEY_ENDSTRING.
72  *  HASH_GIVE_INIT_KEY  void init_key(node *,key) -- initialize key in a newly
73  *                      created node. Defaults: assignment for atomic keys
74  *                      and static strings, strcpy for end-allocated strings.
75  *  HASH_GIVE_INIT_DATA void init_data(node *) -- initialize data fields in a
76  *                      newly created node. Very useful for lookup operations.
77  *  HASH_GIVE_ALLOC     void *alloc(unsigned int size) -- allocate space for
78  *                      a node. Default is either normal or pooled allocation
79  *                      depending on whether we want deletions.
80  *                      void free(void *) -- the converse.
81  *
82  *  ... and a couple of extra parameters:
83  *
84  *  HASH_NOCASE         string comparisons should be case-insensitive.
85  *  HASH_DEFAULT_SIZE=n initially, use hash table of approx. `n' entries.
86  *  HASH_CONSERVE_SPACE use as little space as possible.
87  *  HASH_FN_BITS=n      The hash function gives only `n' significant bits.
88  *  HASH_ATOMIC_TYPE=t  Atomic values are of type `t' instead of int.
89  *  HASH_USE_POOL=pool  Allocate all nodes from given mempool.
90  *                      Collides with delete/remove functions.
91  *
92  *  You also get a iterator macro at no extra charge:
93  *
94  *  HASH_FOR_ALL(hash_prefix, variable)
95  *    {
96  *      // node *variable gets declared automatically
97  *      do_something_with_node(variable);
98  *      // use HASH_BREAK and HASH_CONTINUE instead of break and continue
99  *      // you must not alter contents of the hash table here
100  *    }
101  *  HASH_END_FOR;
102  *
103  *  Then include "lib/hashtable.h" and voila, you have a hash table
104  *  suiting all your needs (at least those which you've revealed :) ).
105  *
106  *  After including this file, all parameter macros are automatically
107  *  undef'd.
108  */
109
110 #ifndef _SHERLOCK_HASHFUNC_H
111 #include "lib/hashfunc.h"
112 #endif
113
114 #include <string.h>
115
116 #if !defined(HASH_NODE) || !defined(HASH_PREFIX)
117 #error Some of the mandatory configuration macros are missing.
118 #endif
119
120 #define P(x) HASH_PREFIX(x)
121
122 /* Declare buckets and the hash table */
123
124 typedef HASH_NODE P(node);
125
126 typedef struct P(bucket) {
127   struct P(bucket) *next;
128 #ifndef HASH_CONSERVE_SPACE
129   uns hash;
130 #endif
131   P(node) n;
132 } P(bucket);
133
134 struct P(table) {
135   uns hash_size;
136   uns hash_count, hash_max, hash_min, hash_hard_max;
137   P(bucket) **ht;
138 } P(table);
139
140 #define T P(table)
141
142 /* Preset parameters */
143
144 #if defined(HASH_KEY_ATOMIC)
145
146 #define HASH_KEY(x) x HASH_KEY_ATOMIC
147
148 #ifndef HASH_ATOMIC_TYPE
149 #  define HASH_ATOMIC_TYPE int
150 #endif
151 #define HASH_KEY_DECL HASH_ATOMIC_TYPE HASH_KEY( )
152
153 #ifndef HASH_GIVE_HASHFN
154 #  define HASH_GIVE_HASHFN
155    static inline int P(hash) (HASH_ATOMIC_TYPE x)
156    { return hash_int(x); }
157 #endif
158
159 #ifndef HASH_GIVE_EQ
160 #  define HASH_GIVE_EQ
161    static inline int P(eq) (HASH_ATOMIC_TYPE x, HASH_ATOMIC_TYPE y)
162    { return x == y; }
163 #endif
164
165 #ifndef HASH_GIVE_INIT_KEY
166 #  define HASH_GIVE_INIT_KEY
167    static inline void P(init_key) (P(node) *n, HASH_ATOMIC_TYPE k)
168    { HASH_KEY(n->) = k; }
169 #endif
170
171 #ifndef HASH_CONSERVE_SPACE
172 #define HASH_CONSERVE_SPACE
173 #endif
174
175 #elif defined(HASH_KEY_STRING) || defined(HASH_KEY_ENDSTRING)
176
177 #ifdef HASH_KEY_STRING
178 #  define HASH_KEY(x) x HASH_KEY_STRING
179 #  ifndef HASH_GIVE_INIT_KEY
180 #    define HASH_GIVE_INIT_KEY
181      static inline void P(init_key) (P(node) *n, char *k)
182      { HASH_KEY(n->) = k; }
183 #  endif
184 #else
185 #  define HASH_KEY(x) x HASH_KEY_ENDSTRING
186 #  define HASH_GIVE_EXTRA_SIZE
187    static inline int P(extra_size) (char *k)
188    { return strlen(k); }
189 #  ifndef HASH_GIVE_INIT_KEY
190 #    define HASH_GIVE_INIT_KEY
191      static inline void P(init_key) (P(node) *n, char *k)
192      { strcpy(HASH_KEY(n->), k); }
193 #  endif
194 #endif
195 #define HASH_KEY_DECL char *HASH_KEY( )
196
197 #ifndef HASH_GIVE_HASHFN
198 #define HASH_GIVE_HASHFN
199   static inline uns P(hash) (char *k)
200    {
201 #    ifdef HASH_NOCASE
202        return hash_string_nocase(k);
203 #    else
204        return hash_string(k);
205 #    endif
206    }
207 #endif
208
209 #ifndef HASH_GIVE_EQ
210 #  define HASH_GIVE_EQ
211    static inline int P(eq) (char *x, char *y)
212    {
213 #    ifdef HASH_NOCASE
214        return !strcasecmp(x,y);
215 #    else
216        return !strcmp(x,y);
217 #    endif
218    }
219 #endif
220
221 #elif defined(HASH_KEY_COMPLEX)
222
223 #define HASH_KEY(x) HASH_KEY_COMPLEX(x)
224
225 #else
226 #error You forgot to set the hash key type.
227 #endif
228
229 /* Defaults for missing parameters */
230
231 #ifndef HASH_GIVE_HASHFN
232 #error Unable to determine which hash function to use.
233 #endif
234
235 #ifndef HASH_GIVE_EQ
236 #error Unable to determine how to compare two keys.
237 #endif
238
239 #ifdef HASH_GIVE_EXTRA_SIZE
240 /* This trickery is needed to avoid `unused parameter' warnings */
241 #define HASH_EXTRA_SIZE P(extra_size)
242 #else
243 /*
244  *  Beware, C macros are expanded iteratively, not recursively,
245  *  hence we get only a _single_ argument, although the expansion
246  *  of HASH_KEY contains commas.
247  */
248 #define HASH_EXTRA_SIZE(x) 0
249 #endif
250
251 #ifndef HASH_GIVE_INIT_KEY
252 #error Unable to determine how to initialize keys.
253 #endif
254
255 #ifndef HASH_GIVE_INIT_DATA
256 static inline void P(init_data) (P(node) *n UNUSED)
257 {
258 }
259 #endif
260
261 #include <stdlib.h>
262
263 #ifndef HASH_GIVE_ALLOC
264 #ifdef HASH_USE_POOL
265
266 static inline void * P(alloc) (unsigned int size)
267 { return mp_alloc_fast(HASH_USE_POOL, size); }
268
269 #else
270
271 static inline void * P(alloc) (unsigned int size)
272 { return xmalloc(size); }
273
274 static inline void P(free) (void *x)
275 { xfree(x); }
276
277 #endif
278 #endif
279
280 #ifndef HASH_DEFAULT_SIZE
281 #define HASH_DEFAULT_SIZE 32
282 #endif
283
284 #ifndef HASH_FN_BITS
285 #define HASH_FN_BITS 32
286 #endif
287
288 /* Now the operations */
289
290 static void P(alloc_table) (void)
291 {
292   T.hash_size = nextprime(T.hash_size);
293   T.ht = xmalloc(sizeof(void *) * T.hash_size);
294   bzero(T.ht, sizeof(void *) * T.hash_size);
295   if (2*T.hash_size < T.hash_hard_max)
296     T.hash_max = 2*T.hash_size;
297   else
298     T.hash_max = ~0U;
299   if (T.hash_size/2 > HASH_DEFAULT_SIZE)
300     T.hash_min = T.hash_size/4;
301   else
302     T.hash_min = 0;
303 }
304
305 static void P(init) (void)
306 {
307   T.hash_count = 0;
308   T.hash_size = HASH_DEFAULT_SIZE;
309 #if HASH_FN_BITS < 28
310   T.hash_hard_max = 1 << HASH_FN_BITS;
311 #else
312   T.hash_hard_max = 1 << 28;
313 #endif
314   P(alloc_table)();
315 }
316
317 #ifdef HASH_WANT_CLEANUP
318 static void P(cleanup) (void)
319 {
320 #ifndef HASH_USE_POOL
321   uns i;
322   P(bucket) *b, *bb;
323
324   for (i=0; i<T.hash_size; i++)
325     for (b=T.ht[i]; b; b=bb)
326       {
327         bb = b->next;
328         P(free)(b);
329       }
330 #endif
331   xfree(T.ht);
332 }
333 #endif
334
335 static inline uns P(bucket_hash) (P(bucket) *b)
336 {
337 #ifdef HASH_CONSERVE_SPACE
338   return P(hash)(HASH_KEY(b->n.));
339 #else
340   return b->hash;
341 #endif
342 }
343
344 static void P(rehash) (uns size)
345 {
346   P(bucket) *b, *nb;
347   P(bucket) **oldt = T.ht, **newt;
348   uns oldsize = T.hash_size;
349   uns i, h;
350
351   // log(L_DEBUG, "Rehashing %d->%d at count %d", oldsize, size, T.hash_count);
352   T.hash_size = size;
353   P(alloc_table)();
354   newt = T.ht;
355   for (i=0; i<oldsize; i++)
356     {
357       b = oldt[i];
358       while (b)
359         {
360           nb = b->next;
361           h = P(bucket_hash)(b) % T.hash_size;
362           b->next = newt[h];
363           newt[h] = b;
364           b = nb;
365         }
366     }
367   xfree(oldt);
368 }
369
370 #ifdef HASH_WANT_FIND
371 static P(node) * P(find) (HASH_KEY_DECL)
372 {
373   uns h0 = P(hash) (HASH_KEY( ));
374   uns h = h0 % T.hash_size;
375   P(bucket) *b;
376
377   for (b=T.ht[h]; b; b=b->next)
378     {
379       if (
380 #ifndef HASH_CONSERVE_SPACE
381           b->hash == h0 &&
382 #endif
383           P(eq)(HASH_KEY( ), HASH_KEY(b->n.)))
384         return &b->n;
385     }
386   return NULL;
387 }
388 #endif
389
390 #ifdef HASH_WANT_FIND_NEXT
391 static P(node) * P(find_next) (P(node) *start)
392 {
393 #ifndef HASH_CONSERVE_SPACE
394   uns h0 = P(hash) (HASH_KEY(start->));
395 #endif
396   P(bucket) *b = SKIP_BACK(P(bucket), n, start);
397
398   for (b=b->next; b; b=b->next)
399     {
400       if (
401 #ifndef HASH_CONSERVE_SPACE
402           b->hash == h0 &&
403 #endif
404           P(eq)(HASH_KEY(start->), HASH_KEY(b->n.)))
405         return &b->n;
406     }
407   return NULL;
408 }
409 #endif
410
411 #ifdef HASH_WANT_NEW
412 static P(node) * P(new) (HASH_KEY_DECL)
413 {
414   uns h0, h;
415   P(bucket) *b;
416
417   h0 = P(hash) (HASH_KEY( ));
418   h = h0 % T.hash_size;
419   b = P(alloc) (sizeof(struct P(bucket)) + HASH_EXTRA_SIZE(HASH_KEY( )));
420   b->next = T.ht[h];
421   T.ht[h] = b;
422 #ifndef HASH_CONSERVE_SPACE
423   b->hash = h0;
424 #endif
425   P(init_key)(&b->n, HASH_KEY( ));
426   P(init_data)(&b->n);
427   if (T.hash_count++ >= T.hash_max)
428     P(rehash)(2*T.hash_size);
429   return &b->n;
430 }
431 #endif
432
433 #ifdef HASH_WANT_LOOKUP
434 static P(node) * P(lookup) (HASH_KEY_DECL)
435 {
436   uns h0 = P(hash) (HASH_KEY( ));
437   uns h = h0 % T.hash_size;
438   P(bucket) *b;
439
440   for (b=T.ht[h]; b; b=b->next)
441     {
442       if (
443 #ifndef HASH_CONSERVE_SPACE
444           b->hash == h0 &&
445 #endif
446           P(eq)(HASH_KEY( ), HASH_KEY(b->n.)))
447         return &b->n;
448     }
449
450   b = P(alloc) (sizeof(struct P(bucket)) + HASH_EXTRA_SIZE(HASH_KEY( )));
451   b->next = T.ht[h];
452   T.ht[h] = b;
453 #ifndef HASH_CONSERVE_SPACE
454   b->hash = h0;
455 #endif
456   P(init_key)(&b->n, HASH_KEY( ));
457   P(init_data)(&b->n);
458   if (T.hash_count++ >= T.hash_max)
459     P(rehash)(2*T.hash_size);
460   return &b->n;
461 }
462 #endif
463
464 #ifdef HASH_WANT_DELETE
465 static int P(delete) (HASH_KEY_DECL)
466 {
467   uns h0 = P(hash) (HASH_KEY( ));
468   uns h = h0 % T.hash_size;
469   P(bucket) *b, **bb;
470
471   for (bb=&T.ht[h]; b=*bb; bb=&b->next)
472     {
473       if (
474 #ifndef HASH_CONSERVE_SPACE
475           b->hash == h0 &&
476 #endif
477           P(eq)(HASH_KEY( ), HASH_KEY(b->n.)))
478         {
479           *bb = b->next;
480           P(free)(b);
481           if (--T.hash_count < T.hash_min)
482             P(rehash)(T.hash_size/2);
483           return 1;
484         }
485     }
486   return 0;
487 }
488 #endif
489
490 #ifdef HASH_WANT_REMOVE
491 static void P(remove) (P(node) *n)
492 {
493   P(bucket) *x = SKIP_BACK(struct P(bucket), n, n);
494   uns h0 = P(bucket_hash)(x);
495   uns h = h0 % T.hash_size;
496   P(bucket) *b, **bb;
497
498   for (bb=&T.ht[h]; (b=*bb) && b != x; bb=&b->next)
499     ;
500   ASSERT(b);
501   *bb = b->next;
502   P(free)(b);
503   if (--T.hash_count < T.hash_min)
504     P(rehash)(T.hash_size/2);
505 }
506 #endif
507
508 /* And the iterator */
509
510 #ifndef HASH_FOR_ALL
511
512 #define HASH_FOR_ALL(h_px, h_var)                                                       \
513 do {                                                                                    \
514   uns h_slot;                                                                           \
515   struct HASH_GLUE(h_px,bucket) *h_buck;                                                \
516   for (h_slot=0; h_slot < HASH_GLUE(h_px,table).hash_size; h_slot++)                    \
517     for (h_buck = HASH_GLUE(h_px,table).ht[h_slot]; h_buck; h_buck = h_buck->next)      \
518       {                                                                                 \
519         HASH_GLUE(h_px,node) *h_var = &h_buck->n;
520 #define HASH_END_FOR } } while(0)
521 #define HASH_BREAK 
522 #define HASH_CONTINUE continue
523 #define HASH_GLUE(x,y) x##_##y
524
525 #endif
526
527 /* Finally, undefine all the parameters */
528
529 #undef P
530 #undef T
531
532 #undef HASH_ATOMIC_TYPE
533 #undef HASH_CONSERVE_SPACE
534 #undef HASH_DEFAULT_SIZE
535 #undef HASH_EXTRA_SIZE
536 #undef HASH_FN_BITS
537 #undef HASH_GIVE_ALLOC
538 #undef HASH_GIVE_EQ
539 #undef HASH_GIVE_EXTRA_SIZE
540 #undef HASH_GIVE_HASHFN
541 #undef HASH_GIVE_INIT_DATA
542 #undef HASH_GIVE_INIT_KEY
543 #undef HASH_KEY
544 #undef HASH_KEY_ATOMIC
545 #undef HASH_KEY_COMPLEX
546 #undef HASH_KEY_DECL
547 #undef HASH_KEY_ENDSTRING
548 #undef HASH_KEY_STRING
549 #undef HASH_NOCASE
550 #undef HASH_NODE
551 #undef HASH_PREFIX
552 #undef HASH_USE_POOL
553 #undef HASH_WANT_CLEANUP
554 #undef HASH_WANT_DELETE
555 #undef HASH_WANT_FIND
556 #undef HASH_WANT_FIND_NEXT
557 #undef HASH_WANT_LOOKUP
558 #undef HASH_WANT_NEW
559 #undef HASH_WANT_REMOVE