source: branches/port5/ARBDB/adhash.c

Last change on this file was 5907, checked in by westram, 16 years ago
  • removed GBS_free_hash_free_pointer()
    • added GBS_create_dynaval_hash() providing free-function to hash
  • removed GBS_hash_first_element/GBS_hash_next_element() and hash-members needed for them
    • added GBS_hash_next_element_that()
  • GBS_hash_do_sorted_loop writes back result of loop-function (as GBS_hash_do_loop does)
  • GBS_hash_do_loop and GBS_hash_do_sorted_loop now delete elements from hash if loop-function returns 0
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.8 KB
Line 
1/********************************************************************************************
2            Some Hash/Cash Procedures
3********************************************************************************************/
4
5#include <stdio.h>
6#include <stdlib.h>
7/* #include <malloc.h> */
8#include <string.h>
9#include <ctype.h>
10#include <limits.h>
11#include <float.h>
12
13#include "adlocal.h"
14/*#include "arbdb.h"*/
15
16        /* memory management */
17
18struct gbs_hash_entry {
19    char *key;
20    long val;
21    struct gbs_hash_entry *next;
22};
23typedef struct gbs_hash_struct {
24    size_t  size;
25    size_t  nelem;
26    GB_CASE case_sens;
27
28    struct gbs_hash_entry **entries; // the hash table (has 'size' entries)
29
30    void (*freefun)(long val); // function to free hash values (see GBS_create_dynaval_hash)
31
32} gbs_hash;
33
34struct gbs_hashi_entry {
35    long key;
36    long val;
37    struct gbs_hashi_entry *next;
38};
39
40struct gbs_hashi_struct {
41    long size;
42    struct gbs_hashi_entry **entries;
43};
44
45/* prime numbers */
46
47#define KNOWN_PRIMES 279
48static long sorted_primes[KNOWN_PRIMES] = {
49    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,
50    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,
51    991, 1049, 1109, 1171, 1237, 1303, 1373, 1447, 1531, 1613, 1699, 1789, 1889, 1993, 2099, 2213, 2333, 2459, 2591, 2729, 2879,
52    3037, 3203, 3373, 3557, 3761, 3967, 4177, 4397, 4637, 4889, 5147, 5419, 5711, 6029, 6353, 6689, 7043, 7417, 7817, 8231, 8669,
53    9127, 9613, 10133, 10667, 11239, 11831, 12457, 13121, 13829, 14557, 15329, 16139, 16993, 17891, 18839, 19841, 20887, 21991, 23159,
54    24379, 25667, 27031, 28463, 29983, 31567, 33247, 35023, 36871, 38821, 40867, 43019, 45289, 47681, 50207, 52859, 55661, 58601,
55    61687, 64937, 68371, 71971, 75767, 79757, 83969, 88397, 93053, 97961, 103123, 108553, 114269, 120293, 126631, 133303, 140321,
56    147709, 155501, 163697, 172313, 181387, 190979, 201031, 211619, 222773, 234499, 246889, 259907, 273601, 288007, 303187, 319147,
57    335953, 353641, 372263, 391861, 412487, 434201, 457057, 481123, 506449, 533111, 561173, 590713, 621821, 654553, 689021, 725293,
58    763471, 803659, 845969, 890501, 937373, 986717, 1038671, 1093357, 1150909, 1211489, 1275269, 1342403, 1413077, 1487459, 1565747,
59    1648181, 1734937, 1826257, 1922383, 2023577, 2130101, 2242213, 2360243, 2484473, 2615243, 2752889, 2897789, 3050321, 3210871,
60    3379877, 3557773, 3745051, 3942209, 4149703, 4368113, 4598063, 4840103, 5094853, 5363011, 5645279, 5942399, 6255157, 6584377,
61    6930929, 7295719, 7679713, 8083919, 8509433, 8957309, 9428759, 9925021, 10447391, 10997279, 11576087, 12185359, 12826699, 13501819,
62    14212447, 14960471, 15747869, 16576727, 17449207, 18367597, 19334317, 20351927, 21423107, 22550639, 23737523, 24986867, 26301967,
63    27686291, 29143493, 30677363, 32291971, 33991597, 35780639, 37663841, 39646153, 41732809, 43929307, 46241389, 48675167, 51237019,
64    53933713, 56772371, 59760391, 62905681, 66216511, 69701591, 73370107, 77231711, 81296543, 85575313, 90079313, 94820347, 99810899
65};
66
67
68/* define CALC_PRIMES only to expand the above table */
69#if defined(DEBUG)
70/* #define CALC_PRIMES */
71#endif /* DEBUG */
72
73#ifdef CALC_PRIMES
74
75#define CALC_PRIMES_UP_TO 100000000L
76#define PRIME_UNDENSITY   20L   /* the higher, the less primes are stored */
77
78#warning "please don't define CALC_PRIMES permanently"
79
80static unsigned char bit_val[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
81
82static int bit_value(const unsigned char *erastothenes, long num) { // 'num' is odd and lowest 'num' is 3
83    long bit_num  = ((num-1) >> 1)-1; // 3->0 5->1 7->2 etc.
84    long byte_num = bit_num >> 3; // div 8
85    char byte     = erastothenes[byte_num];
86
87    gb_assert(bit_num >= 0);
88    gb_assert((num&1) == 1);    // has to odd
89
90    bit_num = bit_num &  7;
91
92    return (byte & bit_val[bit_num]) ? 1 : 0;
93}
94static void set_bit_value(unsigned char *erastothenes, long num, int val) { // 'num' is odd and lowest 'num' is 3; val is 0 or 1
95    long bit_num  = ((num-1) >> 1)-1; // 3->0 5->1 7->2 etc.
96    long byte_num = bit_num >> 3; // div 8
97    char byte     = erastothenes[byte_num];
98
99    gb_assert(bit_num >= 0);
100    gb_assert((num&1) == 1);    // has to odd
101
102    bit_num = bit_num &  7;
103
104    if (val) {
105        byte |= bit_val[bit_num];
106    }
107    else {
108        byte &= (0xff - bit_val[bit_num]);
109    }
110    erastothenes[byte_num] = byte;
111}
112
113static void calculate_primes_upto() {
114    {
115        long           bits_needed  = CALC_PRIMES_UP_TO/2+1; // only need bits for odd numbers
116        long           bytes_needed = (bits_needed/8)+1;
117        unsigned char *erastothenes = GB_calloc(bytes_needed, 1); // bit = 1 means "is not a prime"
118        long           prime_count  = 0;
119        long           num;
120
121        printf("erastothenes' size = %li\n", bytes_needed);
122
123        if (!erastothenes) {
124            GB_internal_error("out of memory");
125            return;
126        }
127
128        for (num = 3; num <= CALC_PRIMES_UP_TO; num += 2) {
129            if (bit_value(erastothenes, num) == 0) { // is a prime number
130                long num2;
131                prime_count++;
132                for (num2 = num*2; num2 <= CALC_PRIMES_UP_TO; num2 += num) { // with all multiples
133                    if ((num2&1) == 1) { // skip even numbers
134                        set_bit_value(erastothenes, num2, 1);
135                    }
136                }
137            }
138            // otherwise it is no prime and all multiples are already set to 1
139        }
140
141        /* thin out prime numbers (we don't need all of them) */
142        {
143            long prime_count2 = 0;
144            long last_prime   = -1000;
145            int  index;
146            int  printed      = 0;
147
148            for (num = 3; num <= CALC_PRIMES_UP_TO; num += 2) {
149                if (bit_value(erastothenes, num) == 0) { // is a prime number
150                    long diff = num-last_prime;
151                    if ((diff*PRIME_UNDENSITY)<num) {
152                        set_bit_value(erastothenes, num, 1); // delete unneeded prime
153                    }
154                    else {
155                        prime_count2++; // count needed primes
156                        last_prime = num;
157                    }
158                }
159            }
160
161            printf("\nUsing %li prime numbers up to %li:\n\n", prime_count2, CALC_PRIMES_UP_TO);
162            printf("#define KNOWN_PRIMES %li\n", prime_count2);
163            printf("static long sorted_primes[KNOWN_PRIMES] = {\n    ");
164            printed = 4;
165
166            index = 0;
167            for (num = 3; num <= CALC_PRIMES_UP_TO; num += 2) {
168                if (bit_value(erastothenes, num) == 0) { // is a prime number
169                    if (printed>128) {
170                        printf("\n    ");
171                        printed = 4;
172                    }
173                    if (num>INT_MAX) {
174                        printed += printf("%liL, ", num);
175                    }
176                    else {
177                        printed += printf("%li, ", num);
178                    }
179                }
180            }
181            printf("\n};\n\n");
182        }
183
184        free(erastothenes);
185    }
186    fflush(stdout);
187    exit(1);
188}
189
190#endif /* CALC_PRIMES */
191
192long GBS_get_a_prime(long above_or_equal_this) {
193    // return a prime number above_or_equal_this
194    // NOTE: it is not necessarily the next prime number, because we don't calculate all prime numbers!
195
196#if defined(CALC_PRIMES)
197    calculate_primes_upto(above_or_equal_this);
198#endif /* CALC_PRIMES */
199
200    if (sorted_primes[KNOWN_PRIMES-1] >= above_or_equal_this) {
201        int l = 0, h = KNOWN_PRIMES-1;
202
203        while (l < h) {
204            int m = (l+h)/2;
205#if defined(DEBUG) && 0
206            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",
207                   l, m, h, above_or_equal_this, l, sorted_primes[l], m, sorted_primes[m], h, sorted_primes[h]);
208#endif /* DEBUG */
209            gb_assert(l <= m);
210            gb_assert(m <= h);
211            if (sorted_primes[m] > above_or_equal_this) {
212                h = m-1;
213            }
214            else {
215                if (sorted_primes[m] < above_or_equal_this) {
216                    l = m+1;
217                }
218                else {
219                    h = l = m;
220                }
221            }
222        }
223
224        if (sorted_primes[l] < above_or_equal_this) {
225            l++;                // take next
226            gb_assert(l<KNOWN_PRIMES);
227        }
228
229        gb_assert(sorted_primes[l] >= above_or_equal_this);
230        gb_assert(l == 0 || sorted_primes[l-1] < above_or_equal_this);
231
232        return sorted_primes[l];
233    }
234
235    fprintf(stderr, "Warning: GBS_get_a_prime failed for value %li (performance bleed)\n", above_or_equal_this);
236    gb_assert(0); // add more primes to sorted_primes[]
237
238    return above_or_equal_this;
239}
240
241/********************************************************************************************
242                    Some Hash Procedures for [string,long]
243********************************************************************************************/
244
245GB_HASH *GBS_create_hash(long user_size, GB_CASE case_sens) {
246    /* Create a hash of size size, this hash is using linked list to avoid collisions,
247     *  ignore_case == 0 -> 'a != A'
248     *  ignore_case != 0 -> 'a == A'
249     */
250   
251    struct gbs_hash_struct *hs;
252    long                    size = GBS_get_a_prime(user_size); // use next prime number for hash size
253
254    hs            = (struct gbs_hash_struct *)GB_calloc(sizeof(struct gbs_hash_struct),1);
255    hs->size      = size;
256    hs->nelem     = 0;
257    hs->case_sens = case_sens;
258    hs->entries   = (struct gbs_hash_entry **)GB_calloc(sizeof(struct gbs_hash_entry *), size);
259    hs->freefun   = NULL; 
260
261    return hs;
262}
263
264GB_HASH *GBS_create_dynaval_hash(long user_size, GB_CASE case_sens, void (*freefun)(long)) {
265    /* like GBS_create_hash, but values stored in hash get free'd using 'freefun'
266     */
267    GB_HASH *hs = GBS_create_hash(user_size, case_sens);
268    hs->freefun = freefun;
269    return hs;
270}
271
272void GBS_dynaval_free(long val) {
273    free((char*)val);
274}
275
276#if defined(DEBUG)
277static void dump_access(const char *title, GB_HASH *hs, double mean_access) {
278    fprintf(stderr,
279            "%s: size=%zu elements=%zu mean_access=%.2f hash-speed=%.1f%%\n",
280            title, hs->size, hs->nelem, mean_access, 100.0/mean_access);
281}
282#endif /* DEBUG */
283
284void GBS_optimize_hash(GB_HASH *hs) {
285    if (hs->nelem > hs->size) {                     /* hash is overfilled (even full is bad) */
286        size_t new_size = GBS_get_a_prime(hs->nelem*3);
287
288#if defined(DEBUG)
289        dump_access("Optimizing filled hash", hs, GBS_hash_mean_access_costs(hs));
290#endif /* DEBUG */
291
292        if (new_size>hs->size) { // avoid overflow
293            struct gbs_hash_entry **new_entries = GB_calloc(sizeof(struct gbs_hash_entry*), new_size);
294            size_t                  pos;
295
296            for (pos = 0; pos<hs->size; ++pos) {
297                struct gbs_hash_entry *e;
298                struct gbs_hash_entry *next;
299
300                for (e = hs->entries[pos]; e; e = next) {
301                    long new_idx;
302                    next = e->next;
303
304                    GB_CALC_HASH_INDEX(e->key, new_idx, new_size, hs->case_sens);
305
306                    e->next              = new_entries[new_idx];
307                    new_entries[new_idx] = e;
308                }
309            }
310
311            free(hs->entries);
312
313            hs->size    = new_size;
314            hs->entries = new_entries;
315        }
316#if defined(DEBUG)
317        dump_access("Optimized hash        ", hs, GBS_hash_mean_access_costs(hs));
318#endif /* DEBUG */
319
320    }
321}
322
323static long gbs_hash_to_strstruct(const char *key, long val, void *cd_out) {
324    const char           *p;
325    int                   c;
326    struct GBS_strstruct *out = (struct GBS_strstruct*)cd_out;
327   
328    for (p = key; (c=*p) ; p++) {
329        GBS_chrcat(out, c);
330        if (c==':') GBS_chrcat(out, c);
331    }
332    GBS_chrcat(out, ':');
333    GBS_intcat(out, val);
334    GBS_chrcat(out, ' ');
335
336    return val;
337}
338
339char *GBS_hashtab_2_string(GB_HASH *hash) {
340    struct GBS_strstruct *out = GBS_stropen(1024);
341    GBS_hash_do_loop(hash, gbs_hash_to_strstruct, out);
342    return GBS_strclose(out);
343}
344
345
346char *GBS_string_2_hashtab(GB_HASH *hash, char *data){  /* destroys data */
347    char *p,*d,*dp;
348    int c;
349    char *nextp;
350    char *error = 0;
351    char *str;
352    int strlen;
353    long val;
354
355    for ( p = data; p ; p = nextp ){
356        strlen = 0;
357        for (dp = p; (c = *dp); dp++){
358            if (c==':') {
359                if (dp[1] == ':') dp++;
360                else break;
361            }
362            strlen++;
363        }
364        if (*dp) {
365            nextp = strchr(dp,' ');
366            if (nextp) nextp++;
367        }
368        else break;
369
370        str = (char *)GB_calloc(sizeof(char),strlen+1);
371        for (dp = p, d = str; (c = *dp) ; dp++){
372            if (c==':'){
373                if (dp[1] == ':') {
374                    *(d++) = c;
375                    dp++;
376                }else break;
377            }else{
378                *(d++) = c;
379            }
380        }
381        val = atoi(dp+1);
382        GBS_write_hash_no_strdup(hash,str,val);
383    }
384
385    return error;
386}
387
388static struct gbs_hash_entry *find_hash_entry(const GB_HASH *hs, const char *key, size_t *index) {
389    struct gbs_hash_entry *e;
390    if (hs->case_sens == GB_IGNORE_CASE) {
391        GB_CALC_HASH_INDEX_CASE_IGNORED(key,*index,hs->size);
392        for(e=hs->entries[*index];e;e=e->next){
393            if (!strcasecmp(e->key,key)) return e;
394        }
395    }
396    else {
397        GB_CALC_HASH_INDEX_CASE_SENSITIVE(key,*index,hs->size);
398        for(e=hs->entries[*index];e;e=e->next){
399            if (!strcmp(e->key,key)) return e;
400        }
401    }
402    return 0;
403}
404
405long GBS_read_hash(const GB_HASH *hs,const char *key) {
406    size_t                 i;
407    struct gbs_hash_entry *e = find_hash_entry(hs, key, &i);
408
409    return e ? e->val : 0;
410}
411
412static void delete_from_list(GB_HASH *hs, size_t i, struct gbs_hash_entry *e) {
413    // delete the hash entry 'e' from list at index 'i'
414    hs->nelem--;
415    if (hs->entries[i] == e) {
416        hs->entries[i] = e->next;
417    }
418    else {
419        struct gbs_hash_entry *ee;
420        for (ee = hs->entries[i]; ee->next != e; ee = ee->next);
421        if (ee->next == e) {
422            ee->next = e->next;
423        }
424        else {
425            GB_internal_error("Database may be corrupt, hash tables error");
426        }
427    }
428    free(e->key);
429    if (hs->freefun) hs->freefun(e->val);
430    gbm_free_mem((char *)e,sizeof(struct gbs_hash_entry),GBM_HASH_INDEX);
431}
432
433static long write_hash(GB_HASH *hs, char *key, GB_BOOL copyKey, long val) {
434    /* returns the old value (or 0 if key had no entry)
435     * if 'copyKey' == GB_FALSE, 'key' will be freed (now or later) and may be invalid!
436     * if 'copyKey' == GB_TRUE, 'key' will not be touched in any way!
437     */
438
439    size_t          i;
440    struct gbs_hash_entry *e       = find_hash_entry(hs, key, &i);
441    long                   oldval  = 0;
442
443    if (e) {
444        oldval = e->val;
445       
446        if (!val) delete_from_list(hs, i, e); // (val == 0 is not stored, cause 0 is the default value)
447        else      e->val = val;
448
449        if (!copyKey) free(key); // already had an entry -> delete usused mem
450    }
451    else if (val != 0) {        // don't store 0
452        // create new hash entry
453        e       = (struct gbs_hash_entry *)gbm_get_mem(sizeof(struct gbs_hash_entry),GBM_HASH_INDEX);
454        e->next = hs->entries[i];
455        e->key  = copyKey ? strdup(key) : key;
456        e->val  = val;
457
458        hs->entries[i] = e;
459        hs->nelem++;
460    }
461    else {
462        if (!copyKey) free(key); // don't need an entry -> delete usused mem
463    }
464    return oldval;
465}
466
467long GBS_write_hash(GB_HASH *hs, const char *key, long val) {
468    /* returns the old value (or 0 if key had no entry) */
469    return write_hash(hs, (char*)key, GB_TRUE, val);
470}
471
472long GBS_write_hash_no_strdup(GB_HASH *hs, char *key, long val) {
473    /* same as GBS_write_hash, but does no strdup. 'key' is freed later in GBS_free_hash,
474     * so the user has to 'malloc' the string and give control to the hash.
475     * Note: after calling this function 'key' may be invalid!
476    */
477    return write_hash(hs, key, GB_FALSE, val);
478}
479
480long GBS_incr_hash(GB_HASH *hs,const char *key) {
481    /* returns new value */
482    size_t                 i;
483    struct gbs_hash_entry *e = find_hash_entry(hs, key, &i);
484    long                   result;
485
486    if (e) {
487        result = ++e->val;
488        if (!result) delete_from_list(hs, i, e);
489    }
490    else {
491        e       = (struct gbs_hash_entry *)gbm_get_mem(sizeof(struct gbs_hash_entry),GBM_HASH_INDEX);
492        e->next = hs->entries[i];
493        e->key  = strdup(key);
494        e->val  = result = 1;
495
496        hs->entries[i] = e;
497        hs->nelem++;
498    }
499    return result;
500}
501
502#if defined(DEVEL_RALF)
503/* #define DUMP_HASH_ENTRIES */
504#endif /* DEVEL_RALF */
505
506#if defined(DEBUG)
507double GBS_hash_mean_access_costs(GB_HASH *hs) {
508    /* returns the mean access costs of the hash [1.0 .. inf[
509     * 1.0 is optimal
510     * 2.0 means: hash speed is 50% (1/2.0) 
511    */
512    double mean_access = 1.0;
513
514    if (hs->nelem) {
515        int    strcmps_needed = 0;
516        size_t pos;
517
518        for (pos = 0; pos<hs->size; pos++) {
519            int                    strcmps = 1;
520            struct gbs_hash_entry *e;
521
522            for (e = hs->entries[pos]; e; e = e->next) {
523                strcmps_needed += strcmps++;
524            }
525        }
526
527        mean_access = (double)strcmps_needed/hs->nelem;
528    }
529    return mean_access;
530}
531#endif /* DEBUG */
532
533void GBS_free_hash_entries(GB_HASH *hs)
534{
535    long i;
536    long e2;   
537    struct gbs_hash_entry *e, *ee;
538
539    e2 = hs->size;
540
541#if defined(DUMP_HASH_ENTRIES)
542    for (i = 0; i < e2; i++) {
543        printf("hash[%li] =", i);
544        for (e = hs->entries[i]; e; e = e->next) {
545            printf(" '%s'", e->key);
546        }
547        printf("\n");
548    }
549#endif /* DUMP_HASH_ENTRIES */
550
551#if defined(DEBUG)
552    if (e2 >= 30) { // ignore small hashes
553        double mean_access = GBS_hash_mean_access_costs(hs);
554        if (mean_access > 1.5) { // every 2nd access is a collision - increase hash size?
555            dump_access("hash-size-warning", hs, mean_access);
556#if defined(DEVEL_RALF)
557            gb_assert(mean_access<2.0); // hash with 50% speed or less
558#endif /* DEVEL_RALF */
559        }
560    }
561#endif /* DEBUG */
562
563    for (i = 0; i < e2; i++) {
564        for (e = hs->entries[i]; e; e = ee) {
565            free(e->key);
566            if (hs->freefun) hs->freefun(e->val);
567            ee              = e->next;
568            gbm_free_mem((char *)e,sizeof(struct gbs_hash_entry),GBM_HASH_INDEX);
569        }
570        hs->entries[i] = 0;
571    }
572}
573
574void GBS_free_hash(GB_HASH *hs)
575{
576    if (!hs) return;
577    GBS_free_hash_entries(hs);
578    free((char *)hs->entries);
579    free((char *)hs);
580}
581
582/* determine hash quality */
583
584typedef struct {
585    long   count;               // how many stats
586    long   min_size, max_size, sum_size;
587    long   min_nelem, max_nelem, sum_nelem;
588    long   min_collisions, max_collisions, sum_collisions;
589    double min_fill_ratio, max_fill_ratio, sum_fill_ratio;
590    double min_hash_quality, max_hash_quality, sum_hash_quality;
591} gbs_hash_statistic_summary;
592
593static GB_HASH *stat_hash = 0;
594
595static void init_hash_statistic_summary(gbs_hash_statistic_summary *stat) {
596    stat->count          = 0;
597    stat->min_size       = stat->min_nelem = stat->min_collisions = LONG_MAX;
598    stat->max_size       = stat->max_nelem = stat->max_collisions = LONG_MIN;
599    stat->min_fill_ratio = stat->min_hash_quality = DBL_MAX;
600    stat->max_fill_ratio = stat->max_hash_quality = DBL_MIN;
601
602    stat->sum_size       = stat->sum_nelem = stat->sum_collisions = 0;
603    stat->sum_fill_ratio = stat->sum_hash_quality = 0.0;
604}
605
606static gbs_hash_statistic_summary *get_stat_summary(const char *id) {
607    long found;
608    if (!stat_hash) stat_hash = GBS_create_hash(10, GB_MIND_CASE);
609    found                     = GBS_read_hash(stat_hash, id);
610    if (!found) {
611        gbs_hash_statistic_summary *stat = GB_calloc(1, sizeof(*stat));
612        init_hash_statistic_summary(stat);
613        found                            = (long)stat;
614        GBS_write_hash(stat_hash, id, found);
615    }
616
617    return (gbs_hash_statistic_summary*)found;
618}
619
620static void addto_hash_statistic_summary(gbs_hash_statistic_summary *stat, long size, long nelem, long collisions, double fill_ratio, double hash_quality) {
621    stat->count++;
622
623    if (stat->min_size > size) stat->min_size = size;
624    if (stat->max_size < size) stat->max_size = size;
625
626    if (stat->min_nelem > nelem) stat->min_nelem = nelem;
627    if (stat->max_nelem < nelem) stat->max_nelem = nelem;
628
629    if (stat->min_collisions > collisions) stat->min_collisions = collisions;
630    if (stat->max_collisions < collisions) stat->max_collisions = collisions;
631
632    if (stat->min_fill_ratio > fill_ratio) stat->min_fill_ratio = fill_ratio;
633    if (stat->max_fill_ratio < fill_ratio) stat->max_fill_ratio = fill_ratio;
634
635    if (stat->min_hash_quality > hash_quality) stat->min_hash_quality = hash_quality;
636    if (stat->max_hash_quality < hash_quality) stat->max_hash_quality = hash_quality;
637
638    stat->sum_size         += size;
639    stat->sum_nelem        += nelem;
640    stat->sum_collisions       += collisions;
641    stat->sum_fill_ratio   += fill_ratio;
642    stat->sum_hash_quality += hash_quality;
643}
644
645void GBS_clear_hash_statistic_summary(const char *id) {
646    init_hash_statistic_summary(get_stat_summary(id));
647}
648
649void GBS_print_hash_statistic_summary(const char *id) {
650    gbs_hash_statistic_summary *stat  = get_stat_summary(id);
651    long                        count = stat->count;
652    printf("Statistic summary for %li hashes of type '%s':\n", count, id);
653    printf("- size:          min = %6li ; max = %6li ; mean = %6.1f\n", stat->min_size, stat->max_size, (double)stat->sum_size/count);
654    printf("- nelem:         min = %6li ; max = %6li ; mean = %6.1f\n", stat->min_nelem, stat->max_nelem, (double)stat->sum_nelem/count);
655    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);
656    printf("- collisions:    min = %6li ; max = %6li ; mean = %6.1f\n", stat->min_collisions, stat->max_collisions, (double)stat->sum_collisions/count);
657    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);
658}
659
660void GBS_calc_hash_statistic(GB_HASH *hs, const char *id, int print) {
661    size_t i;
662    long   queues     = 0;
663    long   collisions;
664    double fill_ratio = (double)hs->nelem/hs->size;
665    double hash_quality;
666
667    for (i = 0; i < hs->size; i++) {
668        if (hs->entries[i]) queues++;
669    }
670    collisions = hs->nelem - queues;
671
672    hash_quality = (double)queues/hs->nelem; // no collisions means 100% quality
673
674    if (print != 0) {
675        printf("Statistic for hash '%s':\n", id);
676        printf("- size       = %zu\n", hs->size);
677        printf("- elements   = %zu (fill ratio = %4.1f%%)\n", hs->nelem, fill_ratio*100.0);
678        printf("- collisions = %li (hash quality = %4.1f%%)\n", collisions, hash_quality*100.0);
679    }
680
681    addto_hash_statistic_summary(get_stat_summary(id), hs->size, hs->nelem, collisions, fill_ratio, hash_quality);
682}
683
684void GBS_hash_do_loop(GB_HASH *hs, gb_hash_loop_type func, void *client_data)
685{
686    long i,e2;
687    struct gbs_hash_entry *e, *next;
688    e2 = hs->size;
689    for (i=0;i<e2;i++) {
690        for (e = hs->entries[i]; e; e = next) {
691            next = e->next;
692            if (e->val) {
693                e->val = func(e->key, e->val, client_data);
694                if (!e->val) delete_from_list(hs, i, e);
695            }
696        }
697    }
698}
699
700long GBS_hash_count_elems(GB_HASH *hs) {
701    long e2    = hs->size;
702    long count = 0;
703    long i;
704    struct gbs_hash_entry *e;
705
706    for (i = 0; i<e2; ++i) {
707        for (e=hs->entries[i]; e; e=e->next) {
708            if (e->val) {
709                ++count;
710            }
711        }
712    }
713
714    return count;
715}
716
717long GBS_hash_count_value(GB_HASH *hs, long val) {
718    long e2    = hs->size;
719    long count = 0;
720    long i;
721    struct gbs_hash_entry *e;
722
723    ad_assert(val != 0); // counting zero values makes no sense (cause these are not stored in the hash)
724
725    for (i = 0; i<e2; ++i) {
726        for (e=hs->entries[i]; e; e=e->next) {
727            if (e->val == val) {
728                ++count;
729            }
730        }
731    }
732
733    return count;
734}
735
736const char *GBS_hash_next_element_that(GB_HASH *hs, const char *last_key, GB_BOOL (*condition)(const char *key, long val, void *cd), void *cd) {
737    /* Returns the key of the next element after 'last_key' matching 'condition' (i.e. where condition returns GB_TRUE).
738     * If 'last_key' is NULL, the first matching element is returned.
739     * Returns NULL if no (more) elements match the 'condition'.
740     */
741
742    size_t                 size = hs->size;;
743    size_t                 i    = 0;
744    struct gbs_hash_entry *e    = 0;
745
746    if (last_key) {
747        e = find_hash_entry(hs, last_key, &i);
748        if (!e) return NULL;
749       
750        e = e->next;       // use next entry after 'last_key'
751        if (!e) i++;
752    }
753
754    for (; i<size && !e; ++i) e = hs->entries[i]; // search first/next entry
755
756    while (e) {
757        if ((*condition)(e->key, e->val, cd)) break;
758        e = e->next;
759        if (!e) {
760            for (i++; i<size && !e; ++i) e = hs->entries[i];
761        }
762    }
763
764    return e ? e->key : NULL;
765}
766
767#ifdef __cplusplus
768extern "C" {
769#endif
770
771    int wrap_hashCompare4gb_sort(const void *v0, const void *v1, void *sorter) {
772        const struct gbs_hash_entry *e0 = (const struct gbs_hash_entry*)v0;
773        const struct gbs_hash_entry *e1 = (const struct gbs_hash_entry*)v1;
774
775        return ((gbs_hash_compare_function)sorter)(e0->key, e0->val, e1->key, e1->val);
776    }
777
778#ifdef __cplusplus
779}
780#endif
781
782void GBS_hash_do_sorted_loop(GB_HASH *hs, gb_hash_loop_type func, gbs_hash_compare_function sorter, void *client_data) {
783    long   i, j, e2;
784    struct gbs_hash_entry *e, **mtab;
785    e2 = hs->size;
786    mtab = (struct gbs_hash_entry **)GB_calloc(sizeof(void *), hs->nelem);
787    for (j = 0, i = 0; i < e2; i++) {
788        for (e = hs->entries[i]; e; e = e->next) {
789            if (e->val) {
790                mtab[j++] = e;
791            }
792        }
793    }
794    GB_sort((void **) mtab, 0, j, wrap_hashCompare4gb_sort, (void*)sorter);
795    for (i = 0; i < j; i++) {
796        long new_val = func(mtab[i]->key, mtab[i]->val, client_data);
797        if (new_val != mtab[i]->val) GBS_write_hash(hs, mtab[i]->key, new_val);
798    }
799    free((char *)mtab);
800}
801
802int GBS_HCF_sortedByKey(const char *k0, long v0, const char *k1, long v1) {
803    GBUSE(v0);
804    GBUSE(v1);
805    return strcmp(k0, k1);
806}
807
808/********************************************************************************************
809                    Some Hash Procedures for [long,long]
810********************************************************************************************/
811
812long gbs_hashi_index(long key, long size)
813{
814    long x;
815    x = (key * (long long)97)%size;     // make one multiplier a (long long) to avoid
816    if (x<0) x+= size;                  // int overflow and abort if compield with -ftrapv
817    return x;
818}
819
820
821GB_HASHI *GBS_create_hashi(long user_size) {
822    long size = GBS_get_a_prime(user_size);                     // use next prime number for hash size
823    struct gbs_hashi_struct *hs = (struct gbs_hashi_struct *)GB_calloc(sizeof(struct gbs_hashi_struct),1);
824
825    hs->size    = size;
826    hs->entries = (struct gbs_hashi_entry **)GB_calloc(sizeof(struct gbs_hashi_entry *),(size_t)size);
827
828    return hs;
829}
830
831
832long GBS_read_hashi(GB_HASHI *hs,long key) {
833    struct gbs_hashi_entry *e;
834    long                    i = gbs_hashi_index(key,hs->size);
835
836    for(e = hs->entries[i]; e; e = e->next) {
837        if (e->key==key) return e->val;
838    }
839    return 0;
840}
841
842long GBS_write_hashi(GB_HASHI *hs,long key,long val) {
843    struct gbs_hashi_entry *e;
844    long                    i2;
845    long                    i = gbs_hashi_index(key,hs->size);
846
847    if (!val) {
848        struct gbs_hashi_entry *oe;
849        oe = 0;
850        for (e = hs->entries[i]; e; e = e->next) {
851            if (e->key == key) {
852                if (oe) {
853                    oe->next = e->next;
854                } else {
855                    hs->entries[i] = e->next;
856                }
857                gbm_free_mem((char *) e, sizeof(struct gbs_hashi_entry),GBM_HASH_INDEX);
858                return 0;
859            }
860            oe = e;
861        }
862        printf("free %lx not found\n",(long)e);
863        return 0;
864    }
865    for(e=hs->entries[i];e;e=e->next)
866    {
867        if (e->key==key) {
868            i2 = e->val;
869            e->val = val;
870            return i2;
871        }
872    }
873    e = (struct gbs_hashi_entry *)gbm_get_mem(sizeof(struct gbs_hashi_entry),GBM_HASH_INDEX);
874    e->next = hs->entries[i];
875    e->key = key;
876    e->val = val;
877    hs->entries[i] = e;
878    return 0;
879}
880
881void GBS_free_hashi(GB_HASHI *hs) {
882    long                    i;
883    struct gbs_hashi_entry *e,*ee;
884    long                    e2 = hs->size;
885
886    for (i=0;i<e2;i++) {
887        for (e=hs->entries[i];e;e=ee) {
888            ee = e->next;
889            gbm_free_mem((char *)e,sizeof(struct gbs_hashi_entry),GBM_HASH_INDEX);
890        }
891    }
892
893    free ((char *)hs->entries);
894    free ((char *)hs);
895}
896
897
898
899/********************************************************************************************
900            Cache Cache Cache
901********************************************************************************************/
902
903void gb_init_cache(GB_MAIN_TYPE *Main){
904    int i;
905    if (Main->cache.entries) return;
906    Main->cache.entries = (struct gb_cache_entry_struct *)GB_calloc(sizeof(struct gb_cache_entry_struct),
907                                                                    GB_MAX_CACHED_ENTRIES);
908    Main->cache.max_data_size = GB_TOTAL_CACHE_SIZE;
909    Main->cache.max_entries = GB_MAX_CACHED_ENTRIES;
910    for (i=0;i<GB_MAX_CACHED_ENTRIES-1;i++) {
911        Main->cache.entries[i].next = i+1;
912    }
913    Main->cache.firstfree_entry = 1;
914}
915
916char *gb_read_cache(GBDATA *gbd) {
917    GB_MAIN_TYPE *Main;
918    struct gb_cache_struct *cs;
919    long i;
920    long n,p;
921    if (!(i=gbd->cache_index)) return 0;
922    Main = GB_MAIN(gbd);
923    cs = &Main->cache;
924    n = cs->entries[i].next; p = cs->entries[i].prev;
925    /* remove entry from list */
926    if (i == cs->newest_entry) cs->newest_entry = n;
927    if (i == cs->oldest_entry) cs->oldest_entry = p;
928    cs->entries[n].prev = p;
929    cs->entries[p].next = n;
930    /* check validity */
931    if (GB_GET_EXT_UPDATE_DATE(gbd) > cs->entries[i].clock) {
932        freeset(cs->entries[i].data, NULL);
933        cs->sum_data_size -= cs->entries[i].sizeof_data;
934
935        gbd->cache_index = 0;
936
937        /* insert deleted entry in free list */
938        cs->entries[i].next = cs->firstfree_entry;
939        cs->firstfree_entry = i;
940        return 0;
941    }
942
943    /* insert entry on top of list */
944    cs->entries[i].next = cs->newest_entry;
945    cs->entries[cs->newest_entry].prev = i;
946    cs->newest_entry = i;
947    cs->entries[i].prev = 0;
948    if (!cs->oldest_entry) cs->oldest_entry = i;
949
950    return cs->entries[i].data;
951}
952
953void *gb_free_cache(GB_MAIN_TYPE *Main, GBDATA *gbd) {
954    struct gb_cache_struct *cs;
955    long i;
956    long n,p;
957    if (!(i=gbd->cache_index)) return 0;
958    cs = &Main->cache;
959    n = cs->entries[i].next; p = cs->entries[i].prev;
960    /* remove entry from list */
961    if (i == cs->newest_entry) cs->newest_entry = n;
962    if (i == cs->oldest_entry) cs->oldest_entry = p;
963    cs->entries[n].prev = p;
964    cs->entries[p].next = n;
965
966    /* free cache */
967    freeset(cs->entries[i].data, NULL);
968    cs->sum_data_size -= cs->entries[i].sizeof_data;
969
970    gbd->cache_index = 0;
971
972    /* insert deleted entry in free list */
973    cs->entries[i].next = cs->firstfree_entry;
974    cs->firstfree_entry = i;
975    return 0;
976}
977
978char *delete_old_cache_entries(struct gb_cache_struct *cs, long needed_size, long max_data_size)
979     /* call with max_data_size==0 to flush cache */
980{
981    long n,p;
982    long i;
983    char *data = 0;
984
985    while ( ( (!cs->firstfree_entry) || ( needed_size + cs->sum_data_size >= max_data_size))
986            && cs->oldest_entry) {
987        i = cs->oldest_entry;
988        n = cs->entries[i].next; p = cs->entries[i].prev;
989        /* remove entry from list */
990        if (i == cs->newest_entry) cs->newest_entry = n;
991        if (i == cs->oldest_entry) cs->oldest_entry = p;
992        cs->entries[n].prev = p;
993        cs->entries[p].next = n;
994
995        /* insert deleted entry in free list */
996        cs->entries[i].gbd->cache_index = 0;
997        cs->entries[i].next = cs->firstfree_entry;
998        cs->firstfree_entry = i;
999        /* delete all unused memorys */
1000        if (data || ( needed_size != cs->entries[i].sizeof_data)  ) {
1001            free(cs->entries[i].data);
1002        }else{
1003            data = cs->entries[i].data;
1004        }
1005        cs->sum_data_size -= cs->entries[i].sizeof_data;
1006        cs->entries[i].data = 0;
1007    }
1008    return data;
1009}
1010
1011char *gb_flush_cache(GBDATA *gbd)
1012{
1013    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1014    struct gb_cache_struct *cs = &Main->cache;
1015
1016    delete_old_cache_entries(cs, 0, 0);
1017    return 0;
1018}
1019
1020char *gb_alloc_cache_index(GBDATA *gbd,long size) {
1021    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1022    struct gb_cache_struct *cs = &Main->cache;
1023    long i;
1024    char *data = 0;
1025
1026    data = delete_old_cache_entries(cs, size, cs->max_data_size); /* delete enough old memory */
1027
1028    i = cs->firstfree_entry;
1029    if (!i) {
1030        GB_internal_error("internal cache error");
1031        return 0;
1032    }
1033
1034    /* get free element */
1035    cs->firstfree_entry = cs->entries[i].next;
1036    /* insert it on top of used list */
1037    cs->entries[i].next = cs->newest_entry;
1038    cs->entries[cs->newest_entry].prev = i;
1039    cs->newest_entry = i;
1040    cs->entries[i].prev = 0;
1041    if (!cs->oldest_entry) cs->oldest_entry = i;
1042
1043    /* create data */
1044    cs->sum_data_size += size;
1045    if (!data) data = (char *) malloc((int)size);
1046    cs->entries[i].sizeof_data = (int)size;
1047    cs->entries[i].data = data;
1048    cs->entries[i].gbd = gbd;
1049    gbd->cache_index = (short)i;
1050
1051    return data;
1052}
1053
1054char *GB_set_cache_size(GBDATA *gbd, long size){
1055    GB_MAIN(gbd)->cache.max_data_size = size;
1056    return 0;
1057}
Note: See TracBrowser for help on using the repository browser.