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
+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
+}