test-hashmap: squelch gcc compiler warning
[git] / test-hashmap.c
1 #include "cache.h"
2 #include "hashmap.h"
3 #include <stdio.h>
4
5 struct test_entry
6 {
7         struct hashmap_entry ent;
8         /* key and value as two \0-terminated strings */
9         char key[FLEX_ARRAY];
10 };
11
12 static const char *get_value(const struct test_entry *e)
13 {
14         return e->key + strlen(e->key) + 1;
15 }
16
17 static int test_entry_cmp(const struct test_entry *e1,
18                 const struct test_entry *e2, const char* key)
19 {
20         return strcmp(e1->key, key ? key : e2->key);
21 }
22
23 static int test_entry_cmp_icase(const struct test_entry *e1,
24                 const struct test_entry *e2, const char* key)
25 {
26         return strcasecmp(e1->key, key ? key : e2->key);
27 }
28
29 static struct test_entry *alloc_test_entry(int hash, char *key, int klen,
30                 char *value, int vlen)
31 {
32         struct test_entry *entry = malloc(sizeof(struct test_entry) + klen
33                         + vlen + 2);
34         hashmap_entry_init(entry, hash);
35         memcpy(entry->key, key, klen + 1);
36         memcpy(entry->key + klen + 1, value, vlen + 1);
37         return entry;
38 }
39
40 #define HASH_METHOD_FNV 0
41 #define HASH_METHOD_I 1
42 #define HASH_METHOD_IDIV10 2
43 #define HASH_METHOD_0 3
44 #define HASH_METHOD_X2 4
45 #define TEST_SPARSE 8
46 #define TEST_ADD 16
47 #define TEST_SIZE 100000
48
49 static unsigned int hash(unsigned int method, unsigned int i, const char *key)
50 {
51         unsigned int hash = 0;
52         switch (method & 3)
53         {
54         case HASH_METHOD_FNV:
55                 hash = strhash(key);
56                 break;
57         case HASH_METHOD_I:
58                 hash = i;
59                 break;
60         case HASH_METHOD_IDIV10:
61                 hash = i / 10;
62                 break;
63         case HASH_METHOD_0:
64                 hash = 0;
65                 break;
66         }
67
68         if (method & HASH_METHOD_X2)
69                 hash = 2 * hash;
70         return hash;
71 }
72
73 /*
74  * Test performance of hashmap.[ch]
75  * Usage: time echo "perfhashmap method rounds" | test-hashmap
76  */
77 static void perf_hashmap(unsigned int method, unsigned int rounds)
78 {
79         struct hashmap map;
80         char buf[16];
81         struct test_entry **entries;
82         unsigned int *hashes;
83         unsigned int i, j;
84
85         entries = malloc(TEST_SIZE * sizeof(struct test_entry *));
86         hashes = malloc(TEST_SIZE * sizeof(int));
87         for (i = 0; i < TEST_SIZE; i++) {
88                 snprintf(buf, sizeof(buf), "%i", i);
89                 entries[i] = alloc_test_entry(0, buf, strlen(buf), "", 0);
90                 hashes[i] = hash(method, i, entries[i]->key);
91         }
92
93         if (method & TEST_ADD) {
94                 /* test adding to the map */
95                 for (j = 0; j < rounds; j++) {
96                         hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0);
97
98                         /* add entries */
99                         for (i = 0; i < TEST_SIZE; i++) {
100                                 hashmap_entry_init(entries[i], hashes[i]);
101                                 hashmap_add(&map, entries[i]);
102                         }
103
104                         hashmap_free(&map, 0);
105                 }
106         } else {
107                 /* test map lookups */
108                 hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0);
109
110                 /* fill the map (sparsely if specified) */
111                 j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;
112                 for (i = 0; i < j; i++) {
113                         hashmap_entry_init(entries[i], hashes[i]);
114                         hashmap_add(&map, entries[i]);
115                 }
116
117                 for (j = 0; j < rounds; j++) {
118                         for (i = 0; i < TEST_SIZE; i++) {
119                                 struct hashmap_entry key;
120                                 hashmap_entry_init(&key, hashes[i]);
121                                 hashmap_get(&map, &key, entries[i]->key);
122                         }
123                 }
124
125                 hashmap_free(&map, 0);
126         }
127 }
128
129 struct hash_entry
130 {
131         struct hash_entry *next;
132         char key[FLEX_ARRAY];
133 };
134
135 /*
136  * Test performance of hash.[ch]
137  * Usage: time echo "perfhash method rounds" | test-hashmap
138  */
139 static void perf_hash(unsigned int method, unsigned int rounds)
140 {
141         struct hash_table map;
142         char buf[16];
143         struct hash_entry **entries, **res, *entry;
144         unsigned int *hashes;
145         unsigned int i, j;
146
147         entries = malloc(TEST_SIZE * sizeof(struct hash_entry *));
148         hashes = malloc(TEST_SIZE * sizeof(int));
149         for (i = 0; i < TEST_SIZE; i++) {
150                 snprintf(buf, sizeof(buf), "%i", i);
151                 entries[i] = malloc(sizeof(struct hash_entry) + strlen(buf) + 1);
152                 strcpy(entries[i]->key, buf);
153                 hashes[i] = hash(method, i, entries[i]->key);
154         }
155
156         if (method & TEST_ADD) {
157                 /* test adding to the map */
158                 for (j = 0; j < rounds; j++) {
159                         init_hash(&map);
160
161                         /* add entries */
162                         for (i = 0; i < TEST_SIZE; i++) {
163                                 res = (struct hash_entry **) insert_hash(
164                                                 hashes[i], entries[i], &map);
165                                 if (res) {
166                                         entries[i]->next = *res;
167                                         *res = entries[i];
168                                 } else {
169                                         entries[i]->next = NULL;
170                                 }
171                         }
172
173                         free_hash(&map);
174                 }
175         } else {
176                 /* test map lookups */
177                 init_hash(&map);
178
179                 /* fill the map (sparsely if specified) */
180                 j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;
181                 for (i = 0; i < j; i++) {
182                         res = (struct hash_entry **) insert_hash(hashes[i],
183                                         entries[i], &map);
184                         if (res) {
185                                 entries[i]->next = *res;
186                                 *res = entries[i];
187                         } else {
188                                 entries[i]->next = NULL;
189                         }
190                 }
191
192                 for (j = 0; j < rounds; j++) {
193                         for (i = 0; i < TEST_SIZE; i++) {
194                                 entry = lookup_hash(hashes[i], &map);
195                                 while (entry) {
196                                         if (!strcmp(entries[i]->key, entry->key))
197                                                 break;
198                                         entry = entry->next;
199                                 }
200                         }
201                 }
202
203                 free_hash(&map);
204
205         }
206 }
207
208 #define DELIM " \t\r\n"
209
210 /*
211  * Read stdin line by line and print result of commands to stdout:
212  *
213  * hash key -> strhash(key) memhash(key) strihash(key) memihash(key)
214  * put key value -> NULL / old value
215  * get key -> NULL / value
216  * remove key -> NULL / old value
217  * iterate -> key1 value1\nkey2 value2\n...
218  * size -> tablesize numentries
219  *
220  * perfhashmap method rounds -> test hashmap.[ch] performance
221  * perfhash method rounds -> test hash.[ch] performance
222  */
223 int main(int argc, char *argv[])
224 {
225         char line[1024];
226         struct hashmap map;
227         int icase;
228
229         /* init hash map */
230         icase = argc > 1 && !strcmp("ignorecase", argv[1]);
231         hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase
232                         : test_entry_cmp), 0);
233
234         /* process commands from stdin */
235         while (fgets(line, sizeof(line), stdin)) {
236                 char *cmd, *p1 = NULL, *p2 = NULL;
237                 int l1 = 0, l2 = 0, hash = 0;
238                 struct test_entry *entry;
239
240                 /* break line into command and up to two parameters */
241                 cmd = strtok(line, DELIM);
242                 /* ignore empty lines */
243                 if (!cmd || *cmd == '#')
244                         continue;
245
246                 p1 = strtok(NULL, DELIM);
247                 if (p1) {
248                         l1 = strlen(p1);
249                         hash = icase ? strihash(p1) : strhash(p1);
250                         p2 = strtok(NULL, DELIM);
251                         if (p2)
252                                 l2 = strlen(p2);
253                 }
254
255                 if (!strcmp("hash", cmd) && l1) {
256
257                         /* print results of different hash functions */
258                         printf("%u %u %u %u\n", strhash(p1), memhash(p1, l1),
259                                         strihash(p1), memihash(p1, l1));
260
261                 } else if (!strcmp("add", cmd) && l1 && l2) {
262
263                         /* create entry with key = p1, value = p2 */
264                         entry = alloc_test_entry(hash, p1, l1, p2, l2);
265
266                         /* add to hashmap */
267                         hashmap_add(&map, entry);
268
269                 } else if (!strcmp("put", cmd) && l1 && l2) {
270
271                         /* create entry with key = p1, value = p2 */
272                         entry = alloc_test_entry(hash, p1, l1, p2, l2);
273
274                         /* add / replace entry */
275                         entry = hashmap_put(&map, entry);
276
277                         /* print and free replaced entry, if any */
278                         puts(entry ? get_value(entry) : "NULL");
279                         free(entry);
280
281                 } else if (!strcmp("get", cmd) && l1) {
282
283                         /* setup static key */
284                         struct hashmap_entry key;
285                         hashmap_entry_init(&key, hash);
286
287                         /* lookup entry in hashmap */
288                         entry = hashmap_get(&map, &key, p1);
289
290                         /* print result */
291                         if (!entry)
292                                 puts("NULL");
293                         while (entry) {
294                                 puts(get_value(entry));
295                                 entry = hashmap_get_next(&map, entry);
296                         }
297
298                 } else if (!strcmp("remove", cmd) && l1) {
299
300                         /* setup static key */
301                         struct hashmap_entry key;
302                         hashmap_entry_init(&key, hash);
303
304                         /* remove entry from hashmap */
305                         entry = hashmap_remove(&map, &key, p1);
306
307                         /* print result and free entry*/
308                         puts(entry ? get_value(entry) : "NULL");
309                         free(entry);
310
311                 } else if (!strcmp("iterate", cmd)) {
312
313                         struct hashmap_iter iter;
314                         hashmap_iter_init(&map, &iter);
315                         while ((entry = hashmap_iter_next(&iter)))
316                                 printf("%s %s\n", entry->key, get_value(entry));
317
318                 } else if (!strcmp("size", cmd)) {
319
320                         /* print table sizes */
321                         printf("%u %u\n", map.tablesize, map.size);
322
323                 } else if (!strcmp("perfhashmap", cmd) && l1 && l2) {
324
325                         perf_hashmap(atoi(p1), atoi(p2));
326
327                 } else if (!strcmp("perfhash", cmd) && l1 && l2) {
328
329                         perf_hash(atoi(p1), atoi(p2));
330
331                 } else {
332
333                         printf("Unknown command %s\n", cmd);
334
335                 }
336         }
337
338         hashmap_free(&map, 1);
339         return 0;
340 }