Make the data array part of the hash bucket to save one pointer per bucket
[platform/upstream/rpm.git] / lib / rpmhash.C
1 /**
2  * \file lib/rpmhash.c
3  * Hash table implemenation
4  */
5
6 #include "system.h"
7 #include "debug.h"
8
9 #define Bucket JOIN(HASHTYPE,Buket)
10 #define Bucket_s JOIN(HASHTYPE,Buket_s)
11
12 typedef struct  Bucket_s * Bucket;
13
14 /**
15  */
16 struct  Bucket_s {
17     Bucket next;        /*!< pointer to next item in bucket */
18     HTKEYTYPE key;      /*!< hash key */
19 #ifdef HTDATATYPE
20     int dataCount;      /*!< data entries */
21     HTDATATYPE data[1]; /*!< data - grows by resizing whole bucket */
22 #endif
23 };
24
25 /**
26  */
27 struct HASHSTRUCT {
28     int numBuckets;                     /*!< number of hash buckets */
29     Bucket * buckets;                   /*!< hash bucket array */
30     hashFunctionType fn;                /*!< generate hash value for key */
31     hashEqualityType eq;                /*!< compare hash keys for equality */
32     hashFreeKey freeKey;
33 #ifdef HTDATATYPE
34     hashFreeData freeData;
35 #endif
36 };
37
38 /**
39  * Find entry in hash table.
40  * @param ht            pointer to hash table
41  * @param key           pointer to key value
42  * @return pointer to hash bucket of key (or NULL)
43  */
44 static
45 Bucket HASHPREFIX(findEntry)(HASHTYPE ht, HTKEYTYPE key)
46 {
47     unsigned int hash;
48     Bucket b;
49
50     hash = ht->fn(key) % ht->numBuckets;
51     b = ht->buckets[hash];
52
53     while (b && b->key && ht->eq(b->key, key))
54         b = b->next;
55
56     return b;
57 }
58
59 HASHTYPE HASHPREFIX(Create)(int numBuckets,
60                             hashFunctionType fn, hashEqualityType eq,
61                             hashFreeKey freeKey
62 #ifdef HTDATATYPE
63 , hashFreeData freeData
64 #endif
65 )
66 {
67     HASHTYPE ht;
68
69     ht = xmalloc(sizeof(*ht));
70     ht->numBuckets = numBuckets;
71     ht->buckets = xcalloc(numBuckets, sizeof(*ht->buckets));
72     ht->freeKey = freeKey;
73 #ifdef HTDATATYPE
74     ht->freeData = freeData;
75 #endif
76     ht->fn = fn;
77     ht->eq = eq;
78     return ht;
79 }
80
81 void HASHPREFIX(AddEntry)(HASHTYPE ht, HTKEYTYPE key
82 #ifdef HTDATATYPE
83 , HTDATATYPE data
84 #endif
85 )
86 {
87     unsigned int hash;
88     Bucket b;
89     Bucket * b_addr;
90
91     hash = ht->fn(key) % ht->numBuckets;
92     b = ht->buckets[hash];
93     b_addr = ht->buckets + hash;
94
95     while (b && b->key && ht->eq(b->key, key)) {
96         b_addr = &(b->next);
97         b = b->next;
98     }
99
100     if (b == NULL) {
101         b = xmalloc(sizeof(*b));
102         b->key = key;
103 #ifdef HTDATATYPE
104         b->dataCount = 1;
105         b->data[0] = data;
106 #endif
107         b->next = ht->buckets[hash];
108         ht->buckets[hash] = b;
109     }
110 #ifdef HTDATATYPE
111     else {
112         // resizing bucket TODO: increase exponentially
113         // Bucket_s already contains space for one dataset
114         b = *b_addr = xrealloc(
115             b, sizeof(*b) + sizeof(b->data[0]) * (b->dataCount));
116         // though increasing dataCount after the resize
117         b->data[b->dataCount++] = data;
118     }
119 #endif
120 }
121
122 HASHTYPE HASHPREFIX(Free)(HASHTYPE ht)
123 {
124     Bucket b, n;
125     int i;
126
127     for (i = 0; i < ht->numBuckets; i++) {
128         b = ht->buckets[i];
129         if (b == NULL)
130             continue;
131         ht->buckets[i] = NULL;
132         if (ht->freeKey)
133             b->key = ht->freeKey(b->key);
134
135         do {
136             n = b->next;
137 #ifdef HTDATATYPE
138             if (ht->freeData) {
139                 int j;
140                 for (j=0; j < b->dataCount; j++ ) {
141                     b->data[j] = ht->freeData(b->data[j]);
142                 }
143             }
144 #endif
145             b = _free(b);
146         } while ((b = n) != NULL);
147     }
148
149     ht->buckets = _free(ht->buckets);
150     ht = _free(ht);
151
152     return NULL;
153 }
154
155 int HASHPREFIX(HasEntry)(HASHTYPE ht, HTKEYTYPE key)
156 {
157     Bucket b;
158
159     if (!(b = HASHPREFIX(findEntry)(ht, key))) return 0; else return 1;
160 }
161
162 #ifdef HTDATATYPE
163
164 int HASHPREFIX(GetEntry)(HASHTYPE ht, HTKEYTYPE key, HTDATATYPE** data,
165                int * dataCount, HTKEYTYPE* tableKey)
166 {
167     Bucket b;
168     int rc = ((b = HASHPREFIX(findEntry)(ht, key)) != NULL);
169
170     if (data)
171         *data = rc ? b->data : NULL;
172     if (dataCount)
173         *dataCount = rc ? b->dataCount : 0;
174     if (tableKey)
175         *tableKey = rc ? b->key : NULL;
176
177     return rc;
178 }
179
180 void HASHPREFIX(PrintStats)(HASHTYPE ht) {
181     int i;
182     Bucket bucket;
183
184     int hashcnt=0, bucketcnt=0, datacnt=0;
185     int maxbuckets=0;
186
187     for (i=0; i<ht->numBuckets; i++) {
188         int buckets = 0;
189         for (bucket=ht->buckets[i]; bucket; bucket=bucket->next){
190             buckets++;
191 #ifdef HTDATATYPE
192             datacnt += bucket->dataCount;
193 #endif
194         }
195         if (maxbuckets < buckets) maxbuckets = buckets;
196         if (buckets) hashcnt++;
197         bucketcnt += buckets;
198     }
199     fprintf(stderr, "Hashsize: %i\n", ht->numBuckets);
200     fprintf(stderr, "Hashbuckets: %i\n", hashcnt);
201     fprintf(stderr, "Keys: %i\n", bucketcnt);
202     fprintf(stderr, "Values: %i\n", datacnt);
203     fprintf(stderr, "Max Keys/Bucket: %i\n", maxbuckets);
204 }
205
206 #endif