source: tags/svn.1.5.4/ARBDB/adhash.cxx

Last change on this file was 8393, checked in by westram, 13 years ago
  • cppchecked (1.53)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.2 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adhash.cxx                                        //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "gb_main.h"
12#include "gb_data.h"
13#include "gb_tune.h"
14#include "gb_hashindex.h"
15
16#include <arb_strbuf.h>
17#include <arb_sort.h>
18
19#include <climits>
20#include <cfloat>
21#include <cctype>
22
23
24struct gbs_hash_entry {
25    char           *key;
26    long            val;
27    gbs_hash_entry *next;
28};
29struct GB_HASH {
30    size_t           size;                          // size of hashtable
31    size_t           nelem;                         // number of elements inserted
32    GB_CASE          case_sens;
33    gbs_hash_entry **entries;                       // the hash table (has 'size' entries)
34
35    void (*freefun)(long val); // function to free hash values (see GBS_create_dynaval_hash)
36
37};
38
39struct numhash_entry {
40    long           key;
41    long           val;
42    numhash_entry *next;
43};
44
45struct GB_NUMHASH {
46    long            size;                           // size of hashtable
47    size_t          nelem;                          // number of elements inserted
48    numhash_entry **entries;
49};
50
51// prime numbers
52
53#define KNOWN_PRIMES 279
54static size_t sorted_primes[KNOWN_PRIMES] = {
55    3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 47, 53, 59, 67, 71, 79, 89, 97, 103, 109, 127, 137, 149, 157, 167, 179, 191, 211, 
56    223, 239, 257, 271, 293, 311, 331, 349, 373, 397, 419, 443, 467, 499, 541, 571, 607, 641, 677, 719, 757, 797, 839, 887, 937, 
57    991, 1049, 1109, 1171, 1237, 1303, 1373, 1447, 1531, 1613, 1699, 1789, 1889, 1993, 2099, 2213, 2333, 2459, 2591, 2729, 2879, 
58    3037, 3203, 3373, 3557, 3761, 3967, 4177, 4397, 4637, 4889, 5147, 5419, 5711, 6029, 6353, 6689, 7043, 7417, 7817, 8231, 8669, 
59    9127, 9613, 10133, 10667, 11239, 11831, 12457, 13121, 13829, 14557, 15329, 16139, 16993, 17891, 18839, 19841, 20887, 21991, 23159, 
60    24379, 25667, 27031, 28463, 29983, 31567, 33247, 35023, 36871, 38821, 40867, 43019, 45289, 47681, 50207, 52859, 55661, 58601, 
61    61687, 64937, 68371, 71971, 75767, 79757, 83969, 88397, 93053, 97961, 103123, 108553, 114269, 120293, 126631, 133303, 140321, 
62    147709, 155501, 163697, 172313, 181387, 190979, 201031, 211619, 222773, 234499, 246889, 259907, 273601, 288007, 303187, 319147, 
63    335953, 353641, 372263, 391861, 412487, 434201, 457057, 481123, 506449, 533111, 561173, 590713, 621821, 654553, 689021, 725293, 
64    763471, 803659, 845969, 890501, 937373, 986717, 1038671, 1093357, 1150909, 1211489, 1275269, 1342403, 1413077, 1487459, 1565747, 
65    1648181, 1734937, 1826257, 1922383, 2023577, 2130101, 2242213, 2360243, 2484473, 2615243, 2752889, 2897789, 3050321, 3210871, 
66    3379877, 3557773, 3745051, 3942209, 4149703, 4368113, 4598063, 4840103, 5094853, 5363011, 5645279, 5942399, 6255157, 6584377, 
67    6930929, 7295719, 7679713, 8083919, 8509433, 8957309, 9428759, 9925021, 10447391, 10997279, 11576087, 12185359, 12826699, 13501819, 
68    14212447, 14960471, 15747869, 16576727, 17449207, 18367597, 19334317, 20351927, 21423107, 22550639, 23737523, 24986867, 26301967, 
69    27686291, 29143493, 30677363, 32291971, 33991597, 35780639, 37663841, 39646153, 41732809, 43929307, 46241389, 48675167, 51237019, 
70    53933713, 56772371, 59760391, 62905681, 66216511, 69701591, 73370107, 77231711, 81296543, 85575313, 90079313, 94820347, 99810899 
71};
72
73// define CALC_PRIMES only to expand the above table
74#if defined(DEBUG)
75// #define CALC_PRIMES
76#endif // DEBUG
77
78#ifdef CALC_PRIMES
79
80#define CALC_PRIMES_UP_TO 100000000U
81#define PRIME_UNDENSITY   20U   // the higher, the less primes are stored
82
83#warning "please don't define CALC_PRIMES permanently"
84
85static unsigned char bit_val[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
86
87static int bit_value(const unsigned char *eratosthenes, long num) {
88    // 'num' is odd and lowest 'num' is 3
89    long bit_num  = ((num-1) >> 1)-1; // 3->0 5->1 7->2 etc.
90    long byte_num = bit_num >> 3; // div 8
91    char byte     = eratosthenes[byte_num];
92
93    gb_assert(bit_num >= 0);
94    gb_assert((num&1) == 1);    // has to odd
95
96    bit_num = bit_num &  7;
97
98    return (byte & bit_val[bit_num]) ? 1 : 0;
99}
100static void set_bit_value(unsigned char *eratosthenes, long num, int val) {
101    // 'num' is odd and lowest 'num' is 3; val is 0 or 1
102    long bit_num  = ((num-1) >> 1)-1; // 3->0 5->1 7->2 etc.
103    long byte_num = bit_num >> 3; // div 8
104    char byte     = eratosthenes[byte_num];
105
106    gb_assert(bit_num >= 0);
107    gb_assert((num&1) == 1);    // has to odd
108
109    bit_num = bit_num &  7;
110
111    if (val) {
112        byte |= bit_val[bit_num];
113    }
114    else {
115        byte &= (0xff - bit_val[bit_num]);
116    }
117    eratosthenes[byte_num] = byte;
118}
119
120static void calculate_primes_upto() {
121    {
122        size_t         bits_needed  = CALC_PRIMES_UP_TO/2+1; // only need bits for odd numbers
123        size_t         bytes_needed = (bits_needed/8)+1;
124        unsigned char *eratosthenes = (unsigned char *)GB_calloc(bytes_needed, 1); // bit = 1 means "is not a prime"
125        size_t         prime_count  = 0;
126        size_t         num;
127
128        printf("eratosthenes' size = %zu\n", bytes_needed);
129        GBK_dump_backtrace(stderr, "calculate_primes_upto");
130
131        if (!eratosthenes) {
132            GB_internal_error("out of memory");
133            return;
134        }
135
136        for (num = 3; num <= CALC_PRIMES_UP_TO; num += 2) {
137            if (bit_value(eratosthenes, num) == 0) { // is a prime number
138                size_t num2;
139                prime_count++;
140                for (num2 = num*2; num2 <= CALC_PRIMES_UP_TO; num2 += num) { // with all multiples
141                    if ((num2&1) == 1) { // skip even numbers
142                        set_bit_value(eratosthenes, num2, 1);
143                    }
144                }
145            }
146            // otherwise it is no prime and all multiples are already set to 1
147        }
148
149        // thin out prime numbers (we don't need all of them)
150        {
151            size_t prime_count2 = 0;
152            size_t last_prime   = 1;
153            size_t index;
154            size_t printed      = 0;
155
156            for (num = 3; num <= CALC_PRIMES_UP_TO; num += 2) {
157                if (bit_value(eratosthenes, num) == 0) { // is a prime number
158                    size_t diff = num-last_prime;
159                    if ((diff*PRIME_UNDENSITY)<num) {
160                        set_bit_value(eratosthenes, num, 1); // delete unneeded prime
161                    }
162                    else {
163                        prime_count2++; // count needed primes
164                        last_prime = num;
165                    }
166                }
167            }
168
169            printf("\nUsing %zu prime numbers up to %zu:\n\n", prime_count2, CALC_PRIMES_UP_TO);
170            printf("#define KNOWN_PRIMES %zu\n", prime_count2);
171            printf("static size_t sorted_primes[KNOWN_PRIMES] = {\n    ");
172            printed = 4;
173
174            index = 0;
175            for (num = 3; num <= CALC_PRIMES_UP_TO; num += 2) {
176                if (bit_value(eratosthenes, num) == 0) { // is a prime number
177                    if (printed>128) {
178                        printf("\n    ");
179                        printed = 4;
180                    }
181
182                    if (num>INT_MAX) {
183                        printed += printf("%zuU, ", num);
184                    }
185                    else {
186                        printed += printf("%zu, ", num);
187                    }
188                }
189            }
190            printf("\n};\n\n");
191        }
192
193        free(eratosthenes);
194    }
195    fflush(stdout);
196    exit(1);
197}
198
199#endif // CALC_PRIMES
200
201size_t gbs_get_a_prime(size_t above_or_equal_this) {
202    // return a prime number above_or_equal_this
203    // NOTE: it is not necessarily the next prime number, because we don't calculate all prime numbers!
204
205#if defined(CALC_PRIMES)
206    calculate_primes_upto();
207#endif // CALC_PRIMES
208
209    if (sorted_primes[KNOWN_PRIMES-1] >= above_or_equal_this) {
210        int l = 0, h = KNOWN_PRIMES-1;
211
212        while (l < h) {
213            int m = (l+h)/2;
214#if defined(DEBUG) && 0
215            printf("l=%-3i m=%-3i h=%-3i above_or_equal_this=%li sorted_primes[%i]=%li sorted_primes[%i]=%li sorted_primes[%i]=%li\n",
216                   l, m, h, above_or_equal_this, l, sorted_primes[l], m, sorted_primes[m], h, sorted_primes[h]);
217#endif // DEBUG
218            gb_assert(l <= m);
219            gb_assert(m <= h);
220            if (sorted_primes[m] > above_or_equal_this) {
221                h = m-1;
222            }
223            else {
224                if (sorted_primes[m] < above_or_equal_this) {
225                    l = m+1;
226                }
227                else {
228                    h = l = m;
229                }
230            }
231        }
232
233        if (sorted_primes[l] < above_or_equal_this) {
234            l++;                // take next
235            gb_assert(l<KNOWN_PRIMES);
236        }
237
238        gb_assert(sorted_primes[l] >= above_or_equal_this);
239        gb_assert(l == 0 || sorted_primes[l-1] < above_or_equal_this);
240
241        return sorted_primes[l];
242    }
243
244    fprintf(stderr, "Warning: gbs_get_a_prime failed for value %zu (performance bleed)\n", above_or_equal_this);
245    gb_assert(0); // add more primes to sorted_primes[]
246
247    return above_or_equal_this; // NEED_NO_COV
248}
249
250// -----------------------------------------------
251//      Some Hash Procedures for [string,long]
252
253inline size_t hash_size(size_t estimated_elements) {
254    size_t min_hash_size = 2*estimated_elements;    // -> fill rate ~ 50% -> collisions unlikely
255    size_t next_prime    = gbs_get_a_prime(min_hash_size); // use next prime number
256
257    return next_prime;
258}
259
260
261GB_HASH *GBS_create_hash(long estimated_elements, GB_CASE case_sens) {
262    /*! Create a hash
263     * @param estimated_elements estimated number of elements added to hash (if you add more elements, hash will still work, but get slow)
264     * Uses linked lists to avoid collisions,
265     */
266    GB_HASH *hs;
267    long     size = hash_size(estimated_elements);
268
269    hs            = (GB_HASH*)GB_calloc(sizeof(*hs), 1);
270    hs->size      = size;
271    hs->nelem     = 0;
272    hs->case_sens = case_sens;
273    hs->entries   = (gbs_hash_entry **)GB_calloc(sizeof(gbs_hash_entry *), size);
274    hs->freefun   = NULL;
275
276    return hs;
277}
278
279GB_HASH *GBS_create_dynaval_hash(long estimated_elements, GB_CASE case_sens, void (*freefun)(long)) {
280    //! like GBS_create_hash, but values stored in hash get freed using 'freefun' when hash gets destroyed
281    GB_HASH *hs = GBS_create_hash(estimated_elements, case_sens);
282    hs->freefun = freefun;
283    return hs;
284}
285
286void GBS_dynaval_free(long val) {
287    free((void*)val);
288}
289
290#if defined(DEBUG)
291static void dump_access(const char *title, GB_HASH *hs, double mean_access) {
292    fprintf(stderr,
293            "%s: size=%zu elements=%zu mean_access=%.2f hash-speed=%.1f%%\n",
294            title, hs->size, hs->nelem, mean_access, 100.0/mean_access);
295}
296
297static double GBS_hash_mean_access_costs(GB_HASH *hs) {
298    /* returns the mean access costs of the hash [1.0 .. inf[
299     * 1.0 is optimal
300     * 2.0 means: hash speed is 50% (1/2.0)
301    */
302    double mean_access = 1.0;
303
304    if (hs->nelem) {
305        int    strcmps_needed = 0;
306        size_t pos;
307
308        for (pos = 0; pos<hs->size; pos++) {
309            int             strcmps = 1;
310            gbs_hash_entry *e;
311
312            for (e = hs->entries[pos]; e; e = e->next) {
313                strcmps_needed += strcmps++;
314            }
315        }
316
317        mean_access = (double)strcmps_needed/hs->nelem;
318    }
319    return mean_access;
320}
321#endif // DEBUG
322
323
324void GBS_optimize_hash(GB_HASH *hs) {
325    if (hs->nelem > hs->size) {                     // hash is overfilled (Note: even 50% fillrate is slow)
326        size_t new_size = gbs_get_a_prime(hs->nelem*3);
327
328#if defined(DEBUG)
329        dump_access("Optimizing filled hash", hs, GBS_hash_mean_access_costs(hs));
330#endif // DEBUG
331
332        if (new_size>hs->size) { // avoid overflow
333            gbs_hash_entry **new_entries = (gbs_hash_entry**)GB_calloc(sizeof(*new_entries), new_size);
334            size_t           pos;
335
336            for (pos = 0; pos<hs->size; ++pos) {
337                gbs_hash_entry *e;
338                gbs_hash_entry *next;
339
340                for (e = hs->entries[pos]; e; e = next) {
341                    long new_idx;
342                    next = e->next;
343
344                    GB_CALC_HASH_INDEX(e->key, new_idx, new_size, hs->case_sens);
345
346                    e->next              = new_entries[new_idx];
347                    new_entries[new_idx] = e;
348                }
349            }
350
351            free(hs->entries);
352
353            hs->size    = new_size;
354            hs->entries = new_entries;
355        }
356#if defined(DEBUG)
357        dump_access("Optimized hash        ", hs, GBS_hash_mean_access_costs(hs));
358#endif // DEBUG
359
360    }
361}
362
363static long gbs_hash_to_strstruct(const char *key, long val, void *cd_out) {
364    const char    *p;
365    int            c;
366    GBS_strstruct *out = (GBS_strstruct*)cd_out;
367
368    for (p = key; (c=*p);  p++) {
369        GBS_chrcat(out, c);
370        if (c==':') GBS_chrcat(out, c);
371    }
372    GBS_chrcat(out, ':');
373    GBS_intcat(out, val);
374    GBS_chrcat(out, ' ');
375
376    return val;
377}
378
379char *GBS_hashtab_2_string(GB_HASH *hash) {
380    GBS_strstruct *out = GBS_stropen(1024);
381    GBS_hash_do_loop(hash, gbs_hash_to_strstruct, out);
382    return GBS_strclose(out);
383}
384
385
386#if defined(UNIT_TESTS)
387static void GBS_string_2_hashtab(GB_HASH *hash, char *data) { // currently only used in unit tests below
388    // modifies data
389    char *p, *d, *dp;
390    int   c;
391    char *nextp;
392    char *str;
393    int   strlen;
394    long  val;
395
396    for (p = data; p;   p = nextp) {
397        strlen = 0;
398        for (dp = p; (c = *dp); dp++) {
399            if (c==':') {
400                if (dp[1] == ':') dp++;
401                else break;
402            }
403            strlen++;
404        }
405        if (*dp) {
406            nextp = strchr(dp, ' ');
407            if (nextp) nextp++;
408        }
409        else break;
410
411        str = (char *)GB_calloc(sizeof(char), strlen+1);
412        for (dp = p, d = str; (c = *dp);  dp++) {
413            if (c==':') {
414                if (dp[1] == ':') {
415                    *(d++) = c;
416                    dp++;
417                }
418                else break;
419            }
420            else {
421                *(d++) = c;
422            }
423        }
424        val = atoi(dp+1);
425        GBS_write_hash_no_strdup(hash, str, val);
426    }
427}
428#endif
429
430static gbs_hash_entry *find_hash_entry(const GB_HASH *hs, const char *key, size_t *index) {
431    gbs_hash_entry *e;
432    if (hs->case_sens == GB_IGNORE_CASE) {
433        GB_CALC_HASH_INDEX_CASE_IGNORED(key, *index, hs->size);
434        for (e=hs->entries[*index]; e; e=e->next) {
435            if (!strcasecmp(e->key, key)) return e;
436        }
437    }
438    else {
439        GB_CALC_HASH_INDEX_CASE_SENSITIVE(key, *index, hs->size);
440        for (e=hs->entries[*index]; e; e=e->next) {
441            if (!strcmp(e->key, key)) return e;
442        }
443    }
444    return 0;
445}
446
447long GBS_read_hash(const GB_HASH *hs, const char *key) {
448    size_t          i;
449    gbs_hash_entry *e = find_hash_entry(hs, key, &i);
450
451    return e ? e->val : 0;
452}
453
454static void delete_from_list(GB_HASH *hs, size_t i, gbs_hash_entry *e) {
455    // delete the hash entry 'e' from list at index 'i'
456    hs->nelem--;
457    if (hs->entries[i] == e) {
458        hs->entries[i] = e->next;
459    }
460    else {
461        gbs_hash_entry *ee;
462        for (ee = hs->entries[i]; ee->next != e; ee = ee->next) ;
463        if (ee->next == e) {
464            ee->next = e->next;
465        }
466        else {
467            GB_internal_error("Database may be corrupt, hash tables error"); // NEED_NO_COV
468        }
469    }
470    free(e->key);
471    if (hs->freefun) hs->freefun(e->val);
472    gbm_free_mem(e, sizeof(gbs_hash_entry), GBM_HASH_INDEX);
473}
474
475static long write_hash(GB_HASH *hs, char *key, bool copyKey, long val) {
476    /* returns the old value (or 0 if key had no entry)
477     * if 'copyKey' == false, 'key' will be freed (now or later) and may be invalid!
478     * if 'copyKey' == true, 'key' will not be touched in any way!
479     */
480
481    size_t          i;
482    gbs_hash_entry *e      = find_hash_entry(hs, key, &i);
483    long            oldval = 0;
484
485    if (e) {
486        oldval = e->val;
487
488        if (!val) delete_from_list(hs, i, e); // (val == 0 is not stored, cause 0 is the default value)
489        else      e->val = val;
490
491        if (!copyKey) free(key); // already had an entry -> delete unused mem
492    }
493    else if (val != 0) {        // don't store 0
494        // create new hash entry
495        e       = (gbs_hash_entry *)gbm_get_mem(sizeof(gbs_hash_entry), GBM_HASH_INDEX);
496        e->next = hs->entries[i];
497        e->key  = copyKey ? strdup(key) : key;
498        e->val  = val;
499
500        hs->entries[i] = e;
501        hs->nelem++;
502    }
503    else {
504        if (!copyKey) free(key); // don't need an entry -> delete unused mem
505    }
506    return oldval;
507}
508
509long GBS_write_hash(GB_HASH *hs, const char *key, long val) {
510    // returns the old value (or 0 if key had no entry)
511    return write_hash(hs, (char*)key, true, val);
512}
513
514long GBS_write_hash_no_strdup(GB_HASH *hs, char *key, long val) {
515    /* same as GBS_write_hash, but does no strdup. 'key' is freed later in GBS_free_hash,
516     * so the user has to 'malloc' the string and give control to the hash.
517     * Note: after calling this function 'key' may be invalid!
518     */
519    return write_hash(hs, key, false, val);
520}
521
522long GBS_incr_hash(GB_HASH *hs, const char *key) {
523    // returns new value
524    size_t          i;
525    gbs_hash_entry *e = find_hash_entry(hs, key, &i);
526    long            result;
527
528    if (e) {
529        result = ++e->val;
530        if (!result) delete_from_list(hs, i, e);
531    }
532    else {
533        e       = (gbs_hash_entry *)gbm_get_mem(sizeof(gbs_hash_entry), GBM_HASH_INDEX);
534        e->next = hs->entries[i];
535        e->key  = strdup(key);
536        e->val  = result = 1;
537
538        hs->entries[i] = e;
539        hs->nelem++;
540    }
541    return result;
542}
543
544#if defined(DEVEL_RALF)
545// #define DUMP_HASH_ENTRIES
546#endif // DEVEL_RALF
547
548static void GBS_erase_hash(GB_HASH *hs) {
549    size_t hsize = hs->size;
550
551#if defined(DUMP_HASH_ENTRIES)
552    for (size_t i = 0; i < hsize; i++) {
553        printf("hash[%li] =", i);
554        for (gbs_hash_entry *e = hs->entries[i]; e; e = e->next) {
555            printf(" '%s'", e->key);
556        }
557        printf("\n");
558    }
559#endif // DUMP_HASH_ENTRIES
560
561    // check hash size
562    if (hsize >= 10) { // ignore small hashes
563#if defined(DEBUG)
564        double mean_access = GBS_hash_mean_access_costs(hs);
565        if (mean_access > 1.5) { // every 2nd access is a collision - increase hash size?
566            dump_access("hash-size-warning", hs, mean_access);
567#if defined(DEVEL_RALF) && !defined(UNIT_TESTS)
568            gb_assert(mean_access<2.0);             // hash with 50% speed or less
569#endif // DEVEL_RALF
570        }
571#else
572        if (hs->nelem >= (2*hsize)) {
573            GB_warningf("Performance leak - very slow hash detected (elems=%li, size=%li)\n", hs->nelem, hs->size);
574            GBK_dump_backtrace(stderr, "detected performance leak");
575        }
576#endif // DEBUG
577    }
578
579    for (size_t i = 0; i < hsize; i++) {
580        for (gbs_hash_entry *e = hs->entries[i]; e; ) {
581            free(e->key);
582            if (hs->freefun) hs->freefun(e->val);
583
584            gbs_hash_entry *next = e->next;
585            gbm_free_mem(e, sizeof(gbs_hash_entry), GBM_HASH_INDEX);
586            e = next;
587        }
588        hs->entries[i] = 0;
589    }
590    hs->nelem = 0;
591}
592
593void GBS_free_hash(GB_HASH *hs) {
594    gb_assert(hs);
595    GBS_erase_hash(hs);
596    free(hs->entries);
597    free(hs);
598}
599
600// determine hash quality
601
602struct gbs_hash_statistic_summary {
603    long   count;               // how many stats
604    long   min_size, max_size, sum_size;
605    long   min_nelem, max_nelem, sum_nelem;
606    long   min_collisions, max_collisions, sum_collisions;
607    double min_fill_ratio, max_fill_ratio, sum_fill_ratio;
608    double min_hash_quality, max_hash_quality, sum_hash_quality;
609
610    void init() {
611        count          = 0;
612        min_size       = min_nelem = min_collisions = LONG_MAX;
613        max_size       = max_nelem = max_collisions = LONG_MIN;
614        min_fill_ratio = min_hash_quality = DBL_MAX;
615        max_fill_ratio = max_hash_quality = DBL_MIN;
616
617        sum_size       = sum_nelem = sum_collisions = 0;
618        sum_fill_ratio = sum_hash_quality = 0.0;
619    }
620};
621
622class hash_statistic_manager : virtual Noncopyable {
623    GB_HASH *stat_hash;
624public:
625    hash_statistic_manager() : stat_hash(NULL) { }
626    ~hash_statistic_manager() {
627        if (stat_hash) GBS_free_hash(stat_hash);
628    }
629
630    gbs_hash_statistic_summary *get_stat_summary(const char *id) {
631        if (!stat_hash) stat_hash = GBS_create_dynaval_hash(10, GB_MIND_CASE, GBS_dynaval_free);
632
633        long found = GBS_read_hash(stat_hash, id);
634        if (!found) {
635            gbs_hash_statistic_summary *stat = (gbs_hash_statistic_summary*)GB_calloc(1, sizeof(*stat));
636            stat->init();
637            found = (long)stat;
638            GBS_write_hash(stat_hash, id, found);
639        }
640
641        return (gbs_hash_statistic_summary*)found;
642    }
643};
644
645static hash_statistic_manager hash_stat_man;
646
647static void addto_hash_statistic_summary(gbs_hash_statistic_summary *stat, long size, long nelem, long collisions, double fill_ratio, double hash_quality) {
648    stat->count++;
649
650    if (stat->min_size > size) stat->min_size = size;
651    if (stat->max_size < size) stat->max_size = size;
652
653    if (stat->min_nelem > nelem) stat->min_nelem = nelem;
654    if (stat->max_nelem < nelem) stat->max_nelem = nelem;
655
656    if (stat->min_collisions > collisions) stat->min_collisions = collisions;
657    if (stat->max_collisions < collisions) stat->max_collisions = collisions;
658
659    if (stat->min_fill_ratio > fill_ratio) stat->min_fill_ratio = fill_ratio;
660    if (stat->max_fill_ratio < fill_ratio) stat->max_fill_ratio = fill_ratio;
661
662    if (stat->min_hash_quality > hash_quality) stat->min_hash_quality = hash_quality;
663    if (stat->max_hash_quality < hash_quality) stat->max_hash_quality = hash_quality;
664
665    stat->sum_size         += size;
666    stat->sum_nelem        += nelem;
667    stat->sum_collisions   += collisions;
668    stat->sum_fill_ratio   += fill_ratio;
669    stat->sum_hash_quality += hash_quality;
670}
671
672void GBS_clear_hash_statistic_summary(const char *id) {
673    hash_stat_man.get_stat_summary(id)->init();
674}
675
676void GBS_print_hash_statistic_summary(const char *id) {
677    gbs_hash_statistic_summary *stat  = hash_stat_man.get_stat_summary(id);
678    long                        count = stat->count;
679    printf("Statistic summary for %li hashes of type '%s':\n", count, id);
680    printf("- size:          min = %6li ; max = %6li ; mean = %6.1f\n", stat->min_size, stat->max_size, (double)stat->sum_size/count);
681    printf("- nelem:         min = %6li ; max = %6li ; mean = %6.1f\n", stat->min_nelem, stat->max_nelem, (double)stat->sum_nelem/count);
682    printf("- fill_ratio:    min = %5.1f%% ; max = %5.1f%% ; mean = %5.1f%%\n", stat->min_fill_ratio*100.0, stat->max_fill_ratio*100.0, (double)stat->sum_fill_ratio/count*100.0);
683    printf("- collisions:    min = %6li ; max = %6li ; mean = %6.1f\n", stat->min_collisions, stat->max_collisions, (double)stat->sum_collisions/count);
684    printf("- hash_quality:  min = %5.1f%% ; max = %5.1f%% ; mean = %5.1f%%\n", stat->min_hash_quality*100.0, stat->max_hash_quality*100.0, (double)stat->sum_hash_quality/count*100.0);
685}
686
687void GBS_calc_hash_statistic(GB_HASH *hs, const char *id, int print) {
688    long   queues     = 0;
689    long   collisions;
690    double fill_ratio = (double)hs->nelem/hs->size;
691    double hash_quality;
692
693    for (size_t i = 0; i < hs->size; i++) {
694        if (hs->entries[i]) queues++;
695    }
696    collisions = hs->nelem - queues;
697
698    hash_quality = (double)queues/hs->nelem; // no collisions means 100% quality
699
700    if (print != 0) {
701        printf("Statistic for hash '%s':\n", id);
702        printf("- size       = %zu\n", hs->size);
703        printf("- elements   = %zu (fill ratio = %4.1f%%)\n", hs->nelem, fill_ratio*100.0);
704        printf("- collisions = %li (hash quality = %4.1f%%)\n", collisions, hash_quality*100.0);
705    }
706
707    addto_hash_statistic_summary(hash_stat_man.get_stat_summary(id), hs->size, hs->nelem, collisions, fill_ratio, hash_quality);
708}
709
710void GBS_hash_do_loop(GB_HASH *hs, gb_hash_loop_type func, void *client_data) {
711    size_t hsize = hs->size;
712    for (size_t i=0; i<hsize; i++) {
713        for (gbs_hash_entry *e = hs->entries[i]; e; ) {
714            gbs_hash_entry *next = e->next;
715            if (e->val) {
716                e->val = func(e->key, e->val, client_data);
717                if (!e->val) delete_from_list(hs, i, e);
718            }
719            e = next;
720        }
721    }
722}
723
724#if defined(WARN_TODO)
725#warning rename GBS_hash_count_elems -> GBS_hash_elements
726#endif
727
728size_t GBS_hash_count_elems(GB_HASH *hs) {
729#if defined(DEBUG)
730    // @@@ old code, just left here to ensure hs->nelem is correct --ralf Mar/2010
731    size_t count = 0;
732    size_t hsize = hs->size;
733    for (size_t i = 0; i<hsize; ++i) {
734        gbs_hash_entry *e;
735        for (e=hs->entries[i]; e; e=e->next) {
736            if (e->val) ++count;
737        }
738    }
739
740    gb_assert(count == hs->nelem);
741#else   
742    size_t count = hs->nelem;
743#endif // DEBUG
744
745    return count;
746}
747
748#if defined(UNIT_TESTS)
749static size_t GBS_hash_count_value(GB_HASH *hs, long val) {
750    size_t hsize    = hs->size;
751    size_t count = 0;
752
753    gb_assert(val != 0); // counting zero values makes no sense (cause these are not stored in the hash)
754
755    for (size_t i = 0; i<hsize; ++i) {
756        for (gbs_hash_entry *e=hs->entries[i]; e; e=e->next) {
757            if (e->val == val) {
758                ++count;
759            }
760        }
761    }
762
763    return count;
764}
765#endif
766
767const char *GBS_hash_next_element_that(GB_HASH *hs, const char *last_key, bool (*condition)(const char *key, long val, void *cd), void *cd) {
768    /* Returns the key of the next element after 'last_key' matching 'condition' (i.e. where condition returns true).
769     * If 'last_key' is NULL, the first matching element is returned.
770     * Returns NULL if no (more) elements match the 'condition'.
771     */
772
773    size_t          size = hs->size;
774    size_t          i    = 0;
775    gbs_hash_entry *e    = 0;
776
777    if (last_key) {
778        e = find_hash_entry(hs, last_key, &i);
779        if (!e) return NULL;
780
781        e = e->next;       // use next entry after 'last_key'
782        if (!e) i++;
783    }
784
785    for (; i<size && !e; ++i) e = hs->entries[i]; // search first/next entry
786
787    while (e) {
788        if ((*condition)(e->key, e->val, cd)) break;
789        e = e->next;
790        if (!e) {
791            for (i++; i<size && !e; ++i) e = hs->entries[i];
792        }
793    }
794
795    // cppcheck-suppress nullPointer
796    return e ? e->key : NULL;
797}
798
799static int wrap_hashCompare4gb_sort(const void *v0, const void *v1, void *sorter) {
800    const gbs_hash_entry *e0 = (const gbs_hash_entry*)v0;
801    const gbs_hash_entry *e1 = (const gbs_hash_entry*)v1;
802
803    return ((gbs_hash_compare_function)sorter)(e0->key, e0->val, e1->key, e1->val);
804}
805
806void GBS_hash_do_sorted_loop(GB_HASH *hs, gb_hash_loop_type func, gbs_hash_compare_function sorter, void *client_data) {
807    size_t           hsize = hs->size;
808    gbs_hash_entry **mtab  = (gbs_hash_entry **)GB_calloc(sizeof(void *), hs->nelem);
809   
810    size_t j = 0;
811    for (size_t i = 0; i < hsize; i++) {
812        for (gbs_hash_entry *e = hs->entries[i]; e; e = e->next) {
813            if (e->val) {
814                mtab[j++] = e;
815            }
816        }
817    }
818
819    GB_sort((void**)mtab, 0, j, wrap_hashCompare4gb_sort, (void*)sorter);
820   
821    for (size_t i = 0; i < j; i++) {
822        long new_val = func(mtab[i]->key, mtab[i]->val, client_data);
823        if (new_val != mtab[i]->val) GBS_write_hash(hs, mtab[i]->key, new_val);
824    }
825   
826    free(mtab);
827}
828
829int GBS_HCF_sortedByKey(const char *k0, long /*v0*/, const char *k1, long /*v1*/) {
830    return strcmp(k0, k1);
831}
832
833// ---------------------------------------------
834//      Some Hash Procedures for [long,long]
835
836inline long gbs_numhash_index(long key, long size) {
837    long x;
838    x = (key * (long long)97)%size;     // make one multiplier a (long long) to avoid
839    if (x<0) x += size;                 // int overflow and abort if compiled with -ftrapv
840    return x;
841}
842
843
844GB_NUMHASH *GBS_create_numhash(size_t estimated_elements) {
845    size_t      size = hash_size(estimated_elements);
846    GB_NUMHASH *hs   = (GB_NUMHASH *)GB_calloc(sizeof(*hs), 1);
847
848    hs->size    = size;
849    hs->nelem   = 0;
850    hs->entries = (numhash_entry **)GB_calloc(sizeof(*(hs->entries)), (size_t)size);
851
852    return hs;
853}
854
855long GBS_read_numhash(GB_NUMHASH *hs, long key) {
856    size_t i = gbs_numhash_index(key, hs->size);
857    for (numhash_entry *e = hs->entries[i]; e; e = e->next) {
858        if (e->key==key) return e->val;
859    }
860    return 0;
861}
862
863long GBS_write_numhash(GB_NUMHASH *hs, long key, long val) {
864    size_t i      = gbs_numhash_index(key, hs->size);
865    long   oldval = 0;
866
867    if (val == 0) { // erase
868        numhash_entry **nextPtr = &(hs->entries[i]);
869
870        for (numhash_entry *e = hs->entries[i]; e; e = e->next) {
871            if (e->key == key) {
872                *nextPtr = e->next;                  // unlink entry
873                gbm_free_mem(e, sizeof(*e), GBM_HASH_INDEX);
874                hs->nelem--;
875                return 0;
876            }
877            nextPtr = &(e->next);
878        }
879    }
880    else {
881        for (numhash_entry *e=hs->entries[i]; e; e=e->next) {
882            if (e->key==key) {
883                oldval = e->val; gb_assert(oldval);
884                e->val = val;
885                break;
886            }
887        }
888
889        if (!oldval) {
890            numhash_entry *e = (numhash_entry *)gbm_get_mem(sizeof(*e), GBM_HASH_INDEX);
891
892            e->next = hs->entries[i];
893            e->key  = key;
894            e->val  = val;
895
896            hs->nelem++;
897            hs->entries[i] = e;
898        }
899    }
900    return oldval;
901}
902
903#if defined(UNIT_TESTS)
904static long GBS_numhash_count_elems(GB_NUMHASH *hs) {
905    return hs->nelem;
906}
907#endif
908
909static void GBS_erase_numhash(GB_NUMHASH *hs) {
910    size_t hsize = hs->size;
911
912    for (size_t i=0; i<hsize; i++) {
913        for (numhash_entry *e = hs->entries[i]; e; ) {
914            numhash_entry *next = e->next;
915           
916            gbm_free_mem(e, sizeof(*e), GBM_HASH_INDEX);
917            e = next;
918        }
919    }
920
921    hs->nelem = 0;
922}
923
924void GBS_free_numhash(GB_NUMHASH *hs) {
925    GBS_erase_numhash(hs);
926    free(hs->entries);
927    free(hs);
928}
929
930// --------------------------------------------------------------------------------
931
932#ifdef UNIT_TESTS
933
934#include <test_unit.h>
935
936static long insert_into_hash(const char *key, long val, void *cl_toHash) {
937    GB_HASH *toHash = (GB_HASH*)cl_toHash;
938    GBS_write_hash(toHash, key, val);
939    return val;
940}
941static long erase_from_hash(const char *key, long val, void *cl_fromHash) {
942    GB_HASH *fromHash = (GB_HASH*)cl_fromHash;
943    long     val2     = GBS_read_hash(fromHash, key);
944
945    if (val2 == val) {
946        GBS_write_hash(fromHash, key, 0);
947    }
948    else {
949        printf("value mismatch in hashes_are_equal(): key='%s' val: %li != %li\n", key, val2, val); // NEED_NO_COV
950    }
951    return val;
952}
953
954static bool hashes_are_equal(GB_HASH *h1, GB_HASH *h2) {
955    size_t count1 = GBS_hash_count_elems(h1);
956    size_t count2 = GBS_hash_count_elems(h2);
957
958    bool equal = (count1 == count2);
959    if (equal) {
960        GB_HASH *copy = GBS_create_hash(count1, GB_MIND_CASE);
961       
962        GBS_hash_do_loop(h1, insert_into_hash, copy);
963        GBS_hash_do_loop(h2, erase_from_hash, copy);
964
965        equal = (GBS_hash_count_elems(copy) == 0);
966        GBS_free_hash(copy);
967    }
968    return equal;
969}
970
971class TestData : virtual Noncopyable {
972public:
973    GB_HASH    *mind;
974    GB_HASH    *ignore;
975    GB_NUMHASH *num;
976
977    TestData() {
978        mind   = GBS_create_hash(100, GB_MIND_CASE);
979        ignore = GBS_create_hash(100, GB_IGNORE_CASE);
980        num    = GBS_create_numhash(100);
981    }
982    ~TestData() {
983        GBS_free_numhash(num);
984        GBS_free_hash(ignore);
985        GBS_free_hash(mind);
986    }
987
988    void reset() {
989        GBS_erase_hash(mind);
990        GBS_erase_hash(ignore);
991        GBS_erase_numhash(num);
992    }
993
994    GB_HASH *get_hash(bool case_sens) {
995        return case_sens ? mind : ignore;
996    }
997};
998
999static TestData TEST;
1000
1001void TEST_GBS_write_hash() {
1002    TEST.reset();
1003
1004    for (int case_sens = 0; case_sens <= 1; ++case_sens) {
1005        GB_HASH *hash = TEST.get_hash(case_sens);
1006
1007        GBS_write_hash(hash, "foo", 1);
1008        TEST_ASSERT(GBS_hash_count_elems(hash) == 1);
1009        TEST_ASSERT(GBS_read_hash(hash, "foo") == 1);
1010
1011        GBS_write_hash(hash, "foo", 2);
1012        TEST_ASSERT(GBS_hash_count_elems(hash) == 1);
1013        TEST_ASSERT(GBS_read_hash(hash, "foo") == 2);
1014       
1015        GBS_write_hash(hash, "foo", 0);
1016        TEST_ASSERT(GBS_hash_count_elems(hash) == 0);
1017        TEST_ASSERT(GBS_read_hash(hash, "foo") == 0);
1018
1019        GBS_write_hash(hash, "foo", 1);
1020        GBS_write_hash(hash, "FOO", 2);
1021        GBS_write_hash(hash, "BAR", 1);
1022        GBS_write_hash(hash, "bar", 2);
1023
1024        if (case_sens) {
1025            TEST_ASSERT(GBS_hash_count_elems(hash) == 4);
1026
1027            TEST_ASSERT(GBS_read_hash(hash, "foo") == 1);
1028            TEST_ASSERT(GBS_read_hash(hash, "FOO") == 2);
1029            TEST_ASSERT(GBS_read_hash(hash, "Foo") == 0);
1030           
1031            TEST_ASSERT(GBS_hash_count_value(hash, 1) == 2);
1032            TEST_ASSERT(GBS_hash_count_value(hash, 2) == 2);
1033        }
1034        else {
1035            TEST_ASSERT(GBS_hash_count_elems(hash) == 2);
1036
1037            TEST_ASSERT(GBS_read_hash(hash, "foo") == 2);
1038            TEST_ASSERT(GBS_read_hash(hash, "FOO") == 2);
1039            TEST_ASSERT(GBS_read_hash(hash, "Foo") == 2);
1040
1041            TEST_ASSERT(GBS_hash_count_value(hash, 1) == 0);
1042            TEST_ASSERT(GBS_hash_count_value(hash, 2) == 2);
1043        }
1044
1045        if (case_sens) {
1046            TEST_ASSERT(GBS_read_hash(hash, "foobar") == 0);
1047            GBS_write_hash_no_strdup(hash, strdup("foobar"), 0);
1048            TEST_ASSERT(GBS_read_hash(hash, "foobar") == 0);
1049            GBS_write_hash_no_strdup(hash, strdup("foobar"), 3);
1050            TEST_ASSERT(GBS_read_hash(hash, "foobar") == 3);
1051            GBS_write_hash_no_strdup(hash, strdup("foobar"), 0);
1052            TEST_ASSERT(GBS_read_hash(hash, "foobar") == 0);
1053        }
1054    }
1055}
1056
1057void TEST_GBS_incr_hash() {
1058    TEST.reset();
1059
1060    for (int case_sens = 0; case_sens <= 1; ++case_sens) {
1061        GB_HASH *hash = TEST.get_hash(case_sens);
1062
1063        GBS_incr_hash(hash, "foo");
1064        TEST_ASSERT(GBS_read_hash(hash, "foo") == 1);
1065
1066        GBS_incr_hash(hash, "foo");
1067        TEST_ASSERT(GBS_read_hash(hash, "foo") == 2);
1068
1069        GBS_incr_hash(hash, "FOO");
1070        if (case_sens) {
1071            TEST_ASSERT(GBS_read_hash(hash, "foo") == 2);
1072            TEST_ASSERT(GBS_read_hash(hash, "FOO") == 1);
1073        }
1074        else {
1075            TEST_ASSERT(GBS_read_hash(hash, "foo") == 3);
1076            TEST_ASSERT(GBS_read_hash(hash, "FOO") == 3);
1077        }
1078    }
1079}
1080
1081void TEST_GBS_hashtab_2_string() {
1082    TEST.reset();
1083
1084    for (int case_sens = 0; case_sens <= 1; ++case_sens) {
1085        GB_HASH *hash = TEST.get_hash(case_sens);
1086
1087        GBS_write_hash(hash, "foo", 1);
1088        GBS_write_hash(hash, "bar", 2);
1089        GBS_write_hash(hash, "FOO", 3);
1090        GBS_write_hash(hash, "BAR", 4);
1091        GBS_write_hash(hash, "foo:bar", 3);
1092        GBS_write_hash(hash, "FOO:bar", 4);
1093    }
1094    for (int case_sens = 0; case_sens <= 1; ++case_sens) {
1095        GB_HASH *hash = TEST.get_hash(case_sens);
1096       
1097        char *as_string = GBS_hashtab_2_string(hash);
1098        TEST_ASSERT(as_string);
1099
1100        GB_HASH *hash2 = GBS_create_hash(1000, case_sens ? GB_MIND_CASE : GB_IGNORE_CASE);
1101        GBS_string_2_hashtab(hash2, as_string); 
1102        TEST_ASSERT(hashes_are_equal(hash, hash2));
1103        TEST_ASSERT(hashes_are_equal(hash, hash2));
1104
1105        free(as_string);
1106        GBS_free_hash(hash2);
1107    }
1108
1109    {
1110        GB_HASH *hash      = TEST.get_hash(true);
1111        char    *as_string = GBS_hashtab_2_string(hash);
1112
1113        GB_HASH *hash2 = GBS_create_hash(21, GB_MIND_CASE);
1114        GBS_hash_do_sorted_loop(hash, insert_into_hash, GBS_HCF_sortedByKey, hash2);
1115       
1116        GB_HASH *hash3 = GBS_create_hash(100, GB_MIND_CASE);
1117        GBS_hash_do_sorted_loop(hash, insert_into_hash, GBS_HCF_sortedByKey, hash3);
1118
1119        char *as_string2 = GBS_hashtab_2_string(hash2); 
1120        char *as_string3 = GBS_hashtab_2_string(hash3); 
1121
1122        TEST_ASSERT_EQUAL__BROKEN(as_string, as_string2);
1123        TEST_ASSERT_EQUAL        (as_string, as_string3);
1124
1125        GBS_free_hash(hash3);
1126        GBS_free_hash(hash2);
1127
1128        free(as_string3);
1129        free(as_string2);
1130        free(as_string);
1131    }
1132}
1133
1134inline long key2val(long key, int pass) {
1135    long val;
1136    switch (pass) {
1137        case 1:
1138            val = key/3;
1139            break;
1140        case 2:
1141            val = key*17461;
1142            break;
1143        default :
1144            TEST_ASSERT(0); // NEED_NO_COV
1145    }
1146    return val;
1147}
1148
1149void TEST_numhash() {
1150    GB_NUMHASH *numhash  = GBS_create_numhash(10);
1151    GB_NUMHASH *numhash2 = GBS_create_numhash(10);
1152
1153    const long LOW  = -200;
1154    const long HIGH = 200;
1155    const long STEP = 17;
1156
1157    long added = 0;
1158    for (int pass = 1; pass <= 2; ++pass) {
1159        added = 0;
1160        for (long key = LOW; key <= HIGH; key += STEP) {
1161            long val = key2val(key, pass);
1162            GBS_write_numhash(numhash, key, val);
1163            added++;
1164        }
1165
1166        TEST_ASSERT(GBS_numhash_count_elems(numhash) == added);
1167
1168        for (long key = LOW; key <= HIGH; key += STEP) {
1169            long expected_val = key2val(key, pass);
1170            long val          = GBS_read_numhash(numhash, key);
1171            TEST_ASSERT(expected_val == val);
1172        }
1173    }
1174
1175    TEST_ASSERT(GBS_read_numhash(numhash, -4711) == 0); // not-existing entry
1176
1177    // erase by overwrite:
1178    for (long key = LOW; key <= HIGH; key += STEP) {
1179        GBS_write_numhash(numhash2, key, GBS_read_numhash(numhash, key)); // copy numhash->numhash2
1180        GBS_write_numhash(numhash, key, (long)NULL);
1181    }
1182    TEST_ASSERT(GBS_numhash_count_elems(numhash2) == added);
1183    TEST_ASSERT(GBS_numhash_count_elems(numhash) == 0);
1184
1185    GBS_free_numhash(numhash2);                     // free filled hash
1186    GBS_free_numhash(numhash);                      // free empty hash
1187}
1188
1189
1190static int freeCounter;
1191static void freeDynamicHashElem(long cl_ptr) {
1192    GBS_dynaval_free(cl_ptr);
1193    freeCounter++;
1194}
1195
1196void TEST_GBS_dynaval_hash() {
1197    const int SIZE  = 10;
1198    const int ELEMS = 30;
1199
1200    GB_HASH *dynahash = GBS_create_dynaval_hash(SIZE, GB_MIND_CASE, freeDynamicHashElem);
1201
1202    for (int pass = 1; pass <= 2; ++pass) {
1203        freeCounter = 0;
1204
1205        for (int i = 0; i<ELEMS; ++i) {
1206            char *val    = GBS_global_string_copy("value %i", i);
1207            char *oldval = (char*)GBS_write_hash(dynahash, GBS_global_string("key %i", i), (long)val);
1208            free(oldval);
1209        }
1210
1211        TEST_ASSERT(freeCounter == 0); // overwriting values shall not automatically free them
1212    }
1213
1214    freeCounter = 0;
1215    GBS_free_hash(dynahash);
1216    TEST_ASSERT(freeCounter == ELEMS);
1217}
1218
1219void TEST_GBS_optimize_hash_and_stats() {
1220    const int SIZE = 10;
1221    const int FILL = 70;
1222
1223    GBS_clear_hash_statistic_summary("test");
1224    for (int pass = 1; pass <= 3; ++pass) {
1225        GB_HASH *hash = GBS_create_hash(SIZE, GB_MIND_CASE);
1226
1227        for (int i = 1; i <= FILL; ++i) {
1228            const char *key =  GBS_global_string("%i", i);
1229            GBS_write_hash(hash, key, i);
1230        }
1231        TEST_ASSERT(hash->nelem > hash->size); // ensure hash is overfilled!
1232
1233        switch (pass) {
1234            case 1:                                 // nothing, only free overfilled hash below
1235                break;
1236            case 2:                                 // test overwrite overfilled hash
1237                for (int i = 1; i <= FILL; ++i) {
1238                    const char *key = GBS_global_string("%i", i);
1239                   
1240                    TEST_ASSERT(GBS_read_hash(hash, key) == i);
1241                    GBS_write_hash(hash, key, 0);
1242                    TEST_ASSERT(GBS_read_hash(hash, key) == 0);
1243                }
1244                break;
1245            case 3:                                 // test optimize
1246                GBS_optimize_hash(hash);
1247                TEST_ASSERT(hash->nelem <= hash->size);
1248                break;
1249            default :
1250                TEST_ASSERT(0);                     // NEED_NO_COV
1251                break;
1252        }
1253
1254        GBS_calc_hash_statistic(hash, "test", 1);
1255        GBS_free_hash(hash);
1256    }
1257
1258    GBS_print_hash_statistic_summary("test");
1259}
1260
1261static bool has_value(const char *, long val, void *cd) { return val == (long)cd; }
1262static bool has_value_greater(const char *, long val, void *cd) { return val > (long)cd; }
1263
1264void TEST_GBS_hash_next_element_that() {
1265    TEST.reset();
1266
1267    for (int case_sens = 0; case_sens <= 1; ++case_sens) {
1268        GB_HASH *hash = TEST.get_hash(case_sens);
1269
1270        GBS_write_hash(hash, "foo", 0);
1271        GBS_write_hash(hash, "bar", 1);
1272        GBS_write_hash(hash, "foobar", 2);
1273        GBS_write_hash(hash, "barfoo", 3);
1274
1275#define READ_REVERSE(value) GBS_hash_next_element_that(hash, NULL, has_value, (void*)value)
1276#define ASSERT_READ_REVERSE_RETURNS(value, expected) TEST_ASSERT_EQUAL((const char *)expected, READ_REVERSE(value));
1277
1278        ASSERT_READ_REVERSE_RETURNS(0, NULL);
1279        ASSERT_READ_REVERSE_RETURNS(1, "bar");
1280        ASSERT_READ_REVERSE_RETURNS(2, "foobar");
1281        ASSERT_READ_REVERSE_RETURNS(3, "barfoo");
1282        ASSERT_READ_REVERSE_RETURNS(4, NULL);
1283
1284        const char *key = NULL;
1285        long        sum = 0;
1286
1287        for (int iter = 1; iter <= 3; ++iter) {
1288            key = GBS_hash_next_element_that(hash, key, has_value_greater, (void*)1);
1289            if (iter == 3) TEST_ASSERT(!key);
1290            else {
1291                TEST_ASSERT(key);
1292                sum += GBS_read_hash(hash, key);
1293            }
1294        }
1295        TEST_ASSERT(sum == 5); // sum of all values > 1
1296    }
1297}
1298
1299const size_t MAX_PRIME  = sorted_primes[KNOWN_PRIMES-1];
1300
1301static size_t get_overflown_prime() { return gbs_get_a_prime(MAX_PRIME+1); }
1302#if defined(ASSERTION_USED)
1303static void detect_prime_overflow() { get_overflown_prime(); }
1304#endif // ASSERTION_USED
1305
1306void TEST_hash_specials() {
1307    const size_t SOME_PRIME = 434201;
1308    TEST_ASSERT(gbs_get_a_prime(SOME_PRIME) == SOME_PRIME);
1309    TEST_ASSERT(gbs_get_a_prime(MAX_PRIME) == MAX_PRIME);
1310
1311#if defined(ASSERTION_USED)
1312    TEST_ASSERT_CODE_ASSERTION_FAILS(detect_prime_overflow);
1313#else
1314    TEST_ASSERT(get_overflown_prime() == MAX_PRIME+1);
1315#endif // ASSERTION_USED
1316}
1317
1318#endif
1319
1320
1321
Note: See TracBrowser for help on using the repository browser.