remove old hash.[ch] implementation
[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;
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 #define DELIM " \t\r\n"
130
131 /*
132  * Read stdin line by line and print result of commands to stdout:
133  *
134  * hash key -> strhash(key) memhash(key) strihash(key) memihash(key)
135  * put key value -> NULL / old value
136  * get key -> NULL / value
137  * remove key -> NULL / old value
138  * iterate -> key1 value1\nkey2 value2\n...
139  * size -> tablesize numentries
140  *
141  * perfhashmap method rounds -> test hashmap.[ch] performance
142  */
143 int main(int argc, char *argv[])
144 {
145         char line[1024];
146         struct hashmap map;
147         int icase;
148
149         /* init hash map */
150         icase = argc > 1 && !strcmp("ignorecase", argv[1]);
151         hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase
152                         : test_entry_cmp), 0);
153
154         /* process commands from stdin */
155         while (fgets(line, sizeof(line), stdin)) {
156                 char *cmd, *p1 = NULL, *p2 = NULL;
157                 int l1 = 0, l2 = 0, hash = 0;
158                 struct test_entry *entry;
159
160                 /* break line into command and up to two parameters */
161                 cmd = strtok(line, DELIM);
162                 /* ignore empty lines */
163                 if (!cmd || *cmd == '#')
164                         continue;
165
166                 p1 = strtok(NULL, DELIM);
167                 if (p1) {
168                         l1 = strlen(p1);
169                         hash = icase ? strihash(p1) : strhash(p1);
170                         p2 = strtok(NULL, DELIM);
171                         if (p2)
172                                 l2 = strlen(p2);
173                 }
174
175                 if (!strcmp("hash", cmd) && l1) {
176
177                         /* print results of different hash functions */
178                         printf("%u %u %u %u\n", strhash(p1), memhash(p1, l1),
179                                         strihash(p1), memihash(p1, l1));
180
181                 } else if (!strcmp("add", cmd) && l1 && l2) {
182
183                         /* create entry with key = p1, value = p2 */
184                         entry = alloc_test_entry(hash, p1, l1, p2, l2);
185
186                         /* add to hashmap */
187                         hashmap_add(&map, entry);
188
189                 } else if (!strcmp("put", cmd) && l1 && l2) {
190
191                         /* create entry with key = p1, value = p2 */
192                         entry = alloc_test_entry(hash, p1, l1, p2, l2);
193
194                         /* add / replace entry */
195                         entry = hashmap_put(&map, entry);
196
197                         /* print and free replaced entry, if any */
198                         puts(entry ? get_value(entry) : "NULL");
199                         free(entry);
200
201                 } else if (!strcmp("get", cmd) && l1) {
202
203                         /* setup static key */
204                         struct hashmap_entry key;
205                         hashmap_entry_init(&key, hash);
206
207                         /* lookup entry in hashmap */
208                         entry = hashmap_get(&map, &key, p1);
209
210                         /* print result */
211                         if (!entry)
212                                 puts("NULL");
213                         while (entry) {
214                                 puts(get_value(entry));
215                                 entry = hashmap_get_next(&map, entry);
216                         }
217
218                 } else if (!strcmp("remove", cmd) && l1) {
219
220                         /* setup static key */
221                         struct hashmap_entry key;
222                         hashmap_entry_init(&key, hash);
223
224                         /* remove entry from hashmap */
225                         entry = hashmap_remove(&map, &key, p1);
226
227                         /* print result and free entry*/
228                         puts(entry ? get_value(entry) : "NULL");
229                         free(entry);
230
231                 } else if (!strcmp("iterate", cmd)) {
232
233                         struct hashmap_iter iter;
234                         hashmap_iter_init(&map, &iter);
235                         while ((entry = hashmap_iter_next(&iter)))
236                                 printf("%s %s\n", entry->key, get_value(entry));
237
238                 } else if (!strcmp("size", cmd)) {
239
240                         /* print table sizes */
241                         printf("%u %u\n", map.tablesize, map.size);
242
243                 } else if (!strcmp("perfhashmap", cmd) && l1 && l2) {
244
245                         perf_hashmap(atoi(p1), atoi(p2));
246
247                 } else {
248
249                         printf("Unknown command %s\n", cmd);
250
251                 }
252         }
253
254         hashmap_free(&map, 1);
255         return 0;
256 }