7 struct hashmap_entry ent
;
8 /* key and value as two \0-terminated strings */
12 static const char *get_value(const struct test_entry
*e
)
14 return e
->key
+ strlen(e
->key
) + 1;
17 static int test_entry_cmp(const struct test_entry
*e1
,
18 const struct test_entry
*e2
, const char* key
)
20 return strcmp(e1
->key
, key
? key
: e2
->key
);
23 static int test_entry_cmp_icase(const struct test_entry
*e1
,
24 const struct test_entry
*e2
, const char* key
)
26 return strcasecmp(e1
->key
, key
? key
: e2
->key
);
29 static struct test_entry
*alloc_test_entry(int hash
, char *key
, int klen
,
30 char *value
, int vlen
)
32 struct test_entry
*entry
= malloc(sizeof(struct test_entry
) + klen
34 hashmap_entry_init(entry
, hash
);
35 memcpy(entry
->key
, key
, klen
+ 1);
36 memcpy(entry
->key
+ klen
+ 1, value
, vlen
+ 1);
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
47 #define TEST_SIZE 100000
49 static unsigned int hash(unsigned int method
, unsigned int i
, const char *key
)
60 case HASH_METHOD_IDIV10
:
68 if (method
& HASH_METHOD_X2
)
74 * Test performance of hashmap.[ch]
75 * Usage: time echo "perfhashmap method rounds" | test-hashmap
77 static void perf_hashmap(unsigned int method
, unsigned int rounds
)
81 struct test_entry
**entries
;
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
);
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);
99 for (i
= 0; i
< TEST_SIZE
; i
++) {
100 hashmap_entry_init(entries
[i
], hashes
[i
]);
101 hashmap_add(&map
, entries
[i
]);
104 hashmap_free(&map
, 0);
107 /* test map lookups */
108 hashmap_init(&map
, (hashmap_cmp_fn
) test_entry_cmp
, 0);
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
]);
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
);
125 hashmap_free(&map
, 0);
131 struct hash_entry
*next
;
132 char key
[FLEX_ARRAY
];
136 * Test performance of hash.[ch]
137 * Usage: time echo "perfhash method rounds" | test-hashmap
139 static void perf_hash(unsigned int method
, unsigned int rounds
)
141 struct hash_table map
;
143 struct hash_entry
**entries
, **res
, *entry
;
144 unsigned int *hashes
;
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
);
156 if (method
& TEST_ADD
) {
157 /* test adding to the map */
158 for (j
= 0; j
< rounds
; j
++) {
162 for (i
= 0; i
< TEST_SIZE
; i
++) {
163 res
= (struct hash_entry
**) insert_hash(
164 hashes
[i
], entries
[i
], &map
);
166 entries
[i
]->next
= *res
;
169 entries
[i
]->next
= NULL
;
176 /* test map lookups */
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
],
185 entries
[i
]->next
= *res
;
188 entries
[i
]->next
= NULL
;
192 for (j
= 0; j
< rounds
; j
++) {
193 for (i
= 0; i
< TEST_SIZE
; i
++) {
194 entry
= lookup_hash(hashes
[i
], &map
);
196 if (!strcmp(entries
[i
]->key
, entry
->key
))
208 #define DELIM " \t\r\n"
211 * Read stdin line by line and print result of commands to stdout:
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
220 * perfhashmap method rounds -> test hashmap.[ch] performance
221 * perfhash method rounds -> test hash.[ch] performance
223 int main(int argc
, char *argv
[])
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);
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
;
240 /* break line into command and up to two parameters */
241 cmd
= strtok(line
, DELIM
);
242 /* ignore empty lines */
243 if (!cmd
|| *cmd
== '#')
246 p1
= strtok(NULL
, DELIM
);
249 hash
= icase
? strihash(p1
) : strhash(p1
);
250 p2
= strtok(NULL
, DELIM
);
255 if (!strcmp("hash", cmd
) && l1
) {
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
));
261 } else if (!strcmp("add", cmd
) && l1
&& l2
) {
263 /* create entry with key = p1, value = p2 */
264 entry
= alloc_test_entry(hash
, p1
, l1
, p2
, l2
);
267 hashmap_add(&map
, entry
);
269 } else if (!strcmp("put", cmd
) && l1
&& l2
) {
271 /* create entry with key = p1, value = p2 */
272 entry
= alloc_test_entry(hash
, p1
, l1
, p2
, l2
);
274 /* add / replace entry */
275 entry
= hashmap_put(&map
, entry
);
277 /* print and free replaced entry, if any */
278 puts(entry
? get_value(entry
) : "NULL");
281 } else if (!strcmp("get", cmd
) && l1
) {
283 /* setup static key */
284 struct hashmap_entry key
;
285 hashmap_entry_init(&key
, hash
);
287 /* lookup entry in hashmap */
288 entry
= hashmap_get(&map
, &key
, p1
);
294 puts(get_value(entry
));
295 entry
= hashmap_get_next(&map
, entry
);
298 } else if (!strcmp("remove", cmd
) && l1
) {
300 /* setup static key */
301 struct hashmap_entry key
;
302 hashmap_entry_init(&key
, hash
);
304 /* remove entry from hashmap */
305 entry
= hashmap_remove(&map
, &key
, p1
);
307 /* print result and free entry*/
308 puts(entry
? get_value(entry
) : "NULL");
311 } else if (!strcmp("iterate", cmd
)) {
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
));
318 } else if (!strcmp("size", cmd
)) {
320 /* print table sizes */
321 printf("%u %u\n", map
.tablesize
, map
.size
);
323 } else if (!strcmp("perfhashmap", cmd
) && l1
&& l2
) {
325 perf_hashmap(atoi(p1
), atoi(p2
));
327 } else if (!strcmp("perfhash", cmd
) && l1
&& l2
) {
329 perf_hash(atoi(p1
), atoi(p2
));
333 printf("Unknown command %s\n", cmd
);
338 hashmap_free(&map
, 1);