aboutsummaryrefslogtreecommitdiff
path: root/src/khash.h
blob: a50dc80295306e82b1afbf3cd2fa1ce0f4bb73f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
/* The MIT License
 
   Copyright (c) 2008, 2009 by attractor <attractor@live.co.uk>
 
   Permission is hereby granted, free of charge, to any person obtaining
   a copy of this software and associated documentation files (the
   "Software"), to deal in the Software without restriction, including
   without limitation the rights to use, copy, modify, merge, publish,
   distribute, sublicense, and/or sell copies of the Software, and to
   permit persons to whom the Software is furnished to do so, subject to
   the following conditions:
 
   The above copyright notice and this permission notice shall be
   included in all copies or substantial portions of the Software.
 
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   SOFTWARE.
*/
 
/*
  An example:
 
#include "khash.h"
KHASH_MAP_INIT_INT(32, char)
int main() {
    int ret, is_missing;
    khiter_t k;
    khash_t(32) *h = kh_init(32);
    k = kh_put(32, h, 5, &ret);
    if (!ret) kh_del(32, h, k);
    kh_value(h, k) = 10;
    k = kh_get(32, h, 10);
    is_missing = (k == kh_end(h));
    k = kh_get(32, h, 5);
    kh_del(32, h, k);
    for (k = kh_begin(h); k != kh_end(h); ++k)
        if (kh_exist(h, k)) kh_value(h, k) = 1;
    kh_destroy(32, h);
    return 0;
}
*/
 
/*
  2009-09-26 (0.2.4):
 
    * Improve portability
 
  2008-09-19 (0.2.3):
 
    * Corrected the example
    * Improved interfaces
 
  2008-09-11 (0.2.2):
 
    * Improved speed a little in kh_put()
 
  2008-09-10 (0.2.1):
 
    * Added kh_clear()
    * Fixed a compiling error
 
  2008-09-02 (0.2.0):
 
    * Changed to token concatenation which increases flexibility.
 
  2008-08-31 (0.1.2):
 
    * Fixed a bug in kh_get(), which has not been tested previously.
 
  2008-08-31 (0.1.1):
 
    * Added destructor
*/
 
#ifdef __cplusplus
extern "C" {
#endif

#ifndef __AC_KHASH_H
#define __AC_KHASH_H
 
/*!
  @header
 
  Generic hash table library.
 
  @copyright Heng Li
 */
 
#define AC_VERSION_KHASH_H "0.2.4"
 
#include <stdlib.h>
#include <string.h>
#include <limits.h>
 
/* compipler specific configuration */
 
#if UINT_MAX == 0xffffffffu
typedef unsigned int khint32_t;
#elif ULONG_MAX == 0xffffffffu
typedef unsigned long khint32_t;
#endif
 
#if ULONG_MAX == ULLONG_MAX
typedef unsigned long khint64_t;
#else
typedef unsigned long long khint64_t;
#endif
 
#ifdef _MSC_VER
#define inline __inline
#endif
 
typedef khint32_t khint_t;
typedef khint_t khiter_t;
 
#define __ac_HASH_PRIME_SIZE 32
static const khint32_t __ac_prime_list[__ac_HASH_PRIME_SIZE] =
{
  0ul,          3ul,          11ul,         23ul,         53ul,
  97ul,         193ul,        389ul,        769ul,        1543ul,
  3079ul,       6151ul,       12289ul,      24593ul,      49157ul,
  98317ul,      196613ul,     393241ul,     786433ul,     1572869ul,
  3145739ul,    6291469ul,    12582917ul,   25165843ul,   50331653ul,
  100663319ul,  201326611ul,  402653189ul,  805306457ul,  1610612741ul,
  3221225473ul, 4294967291ul
};
 
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
 
static const double __ac_HASH_UPPER = 0.77;
 
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
    typedef struct {                                                    \
        khint_t n_buckets, size, n_occupied, upper_bound;               \
        khint32_t *flags;                                               \
        khkey_t *keys;                                                  \
        khval_t *vals;                                                  \
    } kh_##name##_t;                                                    \
    static inline kh_##name##_t *kh_init_##name() {                     \
        return (kh_##name##_t*)calloc(1, sizeof(kh_##name##_t));        \
    }                                                                   \
    static inline void kh_destroy_##name(kh_##name##_t *h)              \
    {                                                                   \
        if (h) {                                                        \
            free(h->keys); free(h->flags);                                \
            free(h->vals);                                               \
            free(h);                                                    \
        }                                                               \
    }                                                                   \
    static inline void kh_clear_##name(kh_##name##_t *h)                \
    {                                                                   \
        if (h && h->flags) {                                         \
            memset(h->flags, 0xaa, ((h->n_buckets>>4) + 1) * sizeof(khint32_t)); \
            h->size = h->n_occupied = 0;                              \
        }                                                               \
    }                                                                   \
    static inline khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
    {                                                                   \
        if (h->n_buckets) {                                              \
            khint_t inc, k, i, last;                                    \
            k = __hash_func(key); i = k % h->n_buckets;                  \
            inc = 1 + k % (h->n_buckets - 1); last = i;                  \
            while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
                if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \
                else i += inc;                                          \
                if (i == last) return h->n_buckets;                      \
            }                                                           \
            return __ac_iseither(h->flags, i)? h->n_buckets : i;      \
        } else return 0;                                                \
    }                                                                   \
    static inline void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
    {                                                                   \
        khint32_t *new_flags = 0;                                       \
        khint_t j = 1;                                                  \
        {                                                               \
            khint_t t = __ac_HASH_PRIME_SIZE - 1;                       \
            while (__ac_prime_list[t] > new_n_buckets) --t;              \
            new_n_buckets = __ac_prime_list[t+1];                       \
            if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0;   \
            else {                                                      \
                new_flags = (khint32_t*)malloc(((new_n_buckets>>4) + 1) * sizeof(khint32_t)); \
                memset(new_flags, 0xaa, ((new_n_buckets>>4) + 1) * sizeof(khint32_t)); \
                if (h->n_buckets < new_n_buckets) {                       \
                    h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \
                    if (kh_is_map)                                      \
                        h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \
                }                                                       \
            }                                                           \
        }                                                               \
        if (j) {                                                        \
            for (j = 0; j != h->n_buckets; ++j) {                        \
                if (__ac_iseither(h->flags, j) == 0) {                   \
                    khkey_t key = h->keys[j];                            \
                    khval_t val;                                        \
                    if (kh_is_map) val = h->vals[j];                 \
                    __ac_set_isdel_true(h->flags, j);                    \
                    while (1) {                                         \
                        khint_t inc, k, i;                              \
                        k = __hash_func(key);                           \
                        i = k % new_n_buckets;                          \
                        inc = 1 + k % (new_n_buckets - 1);              \
                        while (!__ac_isempty(new_flags, i)) {           \
                            if (i + inc >= new_n_buckets) i = i + inc - new_n_buckets; \
                            else i += inc;                              \
                        }                                               \
                        __ac_set_isempty_false(new_flags, i);           \
                        if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { \
                            { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
                            if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
                            __ac_set_isdel_true(h->flags, i);            \
                        } else {                                        \
                            h->keys[i] = key;                            \
                            if (kh_is_map) h->vals[i] = val;         \
                            break;                                      \
                        }                                               \
                    }                                                   \
                }                                                       \
            }                                                           \
            if (h->n_buckets > new_n_buckets) {                           \
                h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \
                if (kh_is_map)                                          \
                    h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \
            }                                                           \
            free(h->flags);                                              \
            h->flags = new_flags;                                        \
            h->n_buckets = new_n_buckets;                                \
            h->n_occupied = h->size;                                  \
            h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
        }                                                               \
    }                                                                   \
    static inline khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
    {                                                                   \
        khint_t x;                                                      \
        if (h->n_occupied >= h->upper_bound) {                         \
            if (h->n_buckets > (h->size<<1)) kh_resize_##name(h, h->n_buckets - 1); \
            else kh_resize_##name(h, h->n_buckets + 1);                  \
        }                                                               \
        {                                                               \
            khint_t inc, k, i, site, last;                              \
            x = site = h->n_buckets; k = __hash_func(key); i = k % h->n_buckets; \
            if (__ac_isempty(h->flags, i)) x = i;                        \
            else {                                                      \
                inc = 1 + k % (h->n_buckets - 1); last = i;              \
                while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
                    if (__ac_isdel(h->flags, i)) site = i;               \
                    if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \
                    else i += inc;                                      \
                    if (i == last) { x = site; break; }                 \
                }                                                       \
                if (x == h->n_buckets) {                             \
                    if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
                    else x = i;                                         \
                }                                                       \
            }                                                           \
        }                                                               \
        if (__ac_isempty(h->flags, x)) {                             \
            h->keys[x] = key;                                            \
            __ac_set_isboth_false(h->flags, x);                          \
            ++h->size; ++h->n_occupied;                                   \
            *ret = 1;                                                   \
        } else if (__ac_isdel(h->flags, x)) {                            \
            h->keys[x] = key;                                            \
            __ac_set_isboth_false(h->flags, x);                          \
            ++h->size;                                                   \
            *ret = 2;                                                   \
        } else *ret = 0;                                                \
        return x;                                                       \
    }                                                                   \
    static inline void kh_del_##name(kh_##name##_t *h, khint_t x)       \
    {                                                                   \
        if (x != h->n_buckets && !__ac_iseither(h->flags, x)) {           \
            __ac_set_isdel_true(h->flags, x);                            \
            --h->size;                                                   \
        }                                                               \
    }
 
/* --- BEGIN OF HASH FUNCTIONS --- */
 
/*! @function
  @abstract     Integer hash function
  @param  key   The integer [khint32_t]
  @return       The hash value [khint_t]
 */
#define kh_int_hash_func(key) (khint32_t)(key)
/*! @function
  @abstract     Integer comparison function
 */
#define kh_int_hash_equal(a, b) ((a) == (b))
/*! @function
  @abstract     64-bit integer hash function
  @param  key   The integer [khint64_t]
  @return       The hash value [khint_t]
 */
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
/*! @function
  @abstract     64-bit integer comparison function
 */
#define kh_int64_hash_equal(a, b) ((a) == (b))
/*! @function
  @abstract     const char* hash function
  @param  s     Pointer to a null terminated string
  @return       The hash value
 */
static inline khint_t __ac_X31_hash_string(const char *s)
{
    khint_t h = *s;
    if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s;
    return h;
}
/*! @function
  @abstract     Another interface to const char* hash function
  @param  key   Pointer to a null terminated string [const char*]
  @return       The hash value [khint_t]
 */
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
/*! @function
  @abstract     Const char* comparison function
 */
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
 
/* --- END OF HASH FUNCTIONS --- */
 
/* Other necessary macros... */
 
/*!
  @abstract Type of the hash table.
  @param  name  Name of the hash table [symbol]
 */
#define khash_t(name) kh_##name##_t
 
/*! @function
  @abstract     Initiate a hash table.
  @param  name  Name of the hash table [symbol]
  @return       Pointer to the hash table [khash_t(name)*]
 */
#define kh_init(name) kh_init_##name()
 
/*! @function
  @abstract     Destroy a hash table.
  @param  name  Name of the hash table [symbol]
  @param  h     Pointer to the hash table [khash_t(name)*]
 */
#define kh_destroy(name, h) kh_destroy_##name(h)
 
/*! @function
  @abstract     Reset a hash table without deallocating memory.
  @param  name  Name of the hash table [symbol]
  @param  h     Pointer to the hash table [khash_t(name)*]
 */
#define kh_clear(name, h) kh_clear_##name(h)
 
/*! @function
  @abstract     Resize a hash table.
  @param  name  Name of the hash table [symbol]
  @param  h     Pointer to the hash table [khash_t(name)*]
  @param  s     New size [khint_t]
 */
#define kh_resize(name, h, s) kh_resize_##name(h, s)
 
/*! @function
  @abstract     Insert a key to the hash table.
  @param  name  Name of the hash table [symbol]
  @param  h     Pointer to the hash table [khash_t(name)*]
  @param  k     Key [type of keys]
  @param  r     Extra return code: 0 if the key is present in the hash table;
                1 if the bucket is empty (never used); 2 if the element in
                the bucket has been deleted [int*]
  @return       Iterator to the inserted element [khint_t]
 */
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
 
/*! @function
  @abstract     Retrieve a key from the hash table.
  @param  name  Name of the hash table [symbol]
  @param  h     Pointer to the hash table [khash_t(name)*]
  @param  k     Key [type of keys]
  @return       Iterator to the found element, or kh_end(h) is the element is absent [khint_t]
 */
#define kh_get(name, h, k) kh_get_##name(h, k)
 
/*! @function
  @abstract     Remove a key from the hash table.
  @param  name  Name of the hash table [symbol]
  @param  h     Pointer to the hash table [khash_t(name)*]
  @param  k     Iterator to the element to be deleted [khint_t]
 */
#define kh_del(name, h, k) kh_del_##name(h, k)
 
 
/*! @function
  @abstract     Test whether a bucket contains data.
  @param  h     Pointer to the hash table [khash_t(name)*]
  @param  x     Iterator to the bucket [khint_t]
  @return       1 if containing data; 0 otherwise [int]
 */
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
 
/*! @function
  @abstract     Get key given an iterator
  @param  h     Pointer to the hash table [khash_t(name)*]
  @param  x     Iterator to the bucket [khint_t]
  @return       Key [type of keys]
 */
#define kh_key(h, x) ((h)->keys[x])
 
/*! @function
  @abstract     Get value given an iterator
  @param  h     Pointer to the hash table [khash_t(name)*]
  @param  x     Iterator to the bucket [khint_t]
  @return       Value [type of values]
  @discussion   For hash sets, calling this results in segfault.
 */
#define kh_val(h, x) ((h)->vals[x])
 
/*! @function
  @abstract     Alias of kh_val()
 */
#define kh_value(h, x) ((h)->vals[x])
 
/*! @function
  @abstract     Get the start iterator
  @param  h     Pointer to the hash table [khash_t(name)*]
  @return       The start iterator [khint_t]
 */
#define kh_begin(h) (khint_t)(0)
 
/*! @function
  @abstract     Get the end iterator
  @param  h     Pointer to the hash table [khash_t(name)*]
  @return       The end iterator [khint_t]
 */
#define kh_end(h) ((h)->n_buckets)
 
/*! @function
  @abstract     Get the number of elements in the hash table
  @param  h     Pointer to the hash table [khash_t(name)*]
  @return       Number of elements in the hash table [khint_t]
 */
#define kh_size(h) ((h)->size)
 
/*! @function
  @abstract     Get the number of buckets in the hash table
  @param  h     Pointer to the hash table [khash_t(name)*]
  @return       Number of buckets in the hash table [khint_t]
 */
#define kh_n_buckets(h) ((h)->n_buckets)
 
/* More conenient interfaces */
 
/*! @function
  @abstract     Instantiate a hash set containing integer keys
  @param  name  Name of the hash table [symbol]
 */
#define KHASH_SET_INIT_INT(name)                                        \
    KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
 
/*! @function
  @abstract     Instantiate a hash map containing integer keys
  @param  name  Name of the hash table [symbol]
  @param  khval_t  Type of values [type]
 */
#define KHASH_MAP_INIT_INT(name, khval_t)                               \
    KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
 
/*! @function
  @abstract     Instantiate a hash map containing 64-bit integer keys
  @param  name  Name of the hash table [symbol]
 */
#define KHASH_SET_INIT_INT64(name)                                      \
    KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
 
/*! @function
  @abstract     Instantiate a hash map containing 64-bit integer keys
  @param  name  Name of the hash table [symbol]
  @param  khval_t  Type of values [type]
 */
#define KHASH_MAP_INIT_INT64(name, khval_t)                             \
    KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
 
typedef const char *kh_cstr_t;
/*! @function
  @abstract     Instantiate a hash map containing const char* keys
  @param  name  Name of the hash table [symbol]
 */
#define KHASH_SET_INIT_STR(name)                                        \
    KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
 
/*! @function
  @abstract     Instantiate a hash map containing const char* keys
  @param  name  Name of the hash table [symbol]
  @param  khval_t  Type of values [type]
 */
#define KHASH_MAP_INIT_STR(name, khval_t)                               \
    KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
 
#endif /* __AC_KHASH_H */

#ifdef __cplusplus
}
#endif