1 #include "git-compat-util.h"
6 struct hashmap_entry ent
;
7 /* key and value as two \0-terminated strings */
11 static const char *get_value(const struct test_entry
*e
)
13 return e
->key
+ strlen(e
->key
) + 1;
16 static int test_entry_cmp(const struct test_entry
*e1
,
17 const struct test_entry
*e2
, const char* key
)
19 return strcmp(e1
->key
, key
? key
: e2
->key
);
22 static int test_entry_cmp_icase(const struct test_entry
*e1
,
23 const struct test_entry
*e2
, const char* key
)
25 return strcasecmp(e1
->key
, key
? key
: e2
->key
);
28 static struct test_entry
*alloc_test_entry(int hash
, char *key
, int klen
,
29 char *value
, int vlen
)
31 struct test_entry
*entry
= malloc(sizeof(struct test_entry
) + klen
33 hashmap_entry_init(entry
, hash
);
34 memcpy(entry
->key
, key
, klen
+ 1);
35 memcpy(entry
->key
+ klen
+ 1, value
, vlen
+ 1);
39 #define HASH_METHOD_FNV 0
40 #define HASH_METHOD_I 1
41 #define HASH_METHOD_IDIV10 2
42 #define HASH_METHOD_0 3
43 #define HASH_METHOD_X2 4
46 #define TEST_SIZE 100000
48 static unsigned int hash(unsigned int method
, unsigned int i
, const char *key
)
50 unsigned int hash
= 0;
59 case HASH_METHOD_IDIV10
:
67 if (method
& HASH_METHOD_X2
)
73 * Test performance of hashmap.[ch]
74 * Usage: time echo "perfhashmap method rounds" | test-hashmap
76 static void perf_hashmap(unsigned int method
, unsigned int rounds
)
80 struct test_entry
**entries
;
84 entries
= malloc(TEST_SIZE
* sizeof(struct test_entry
*));
85 hashes
= malloc(TEST_SIZE
* sizeof(int));
86 for (i
= 0; i
< TEST_SIZE
; i
++) {
87 snprintf(buf
, sizeof(buf
), "%i", i
);
88 entries
[i
] = alloc_test_entry(0, buf
, strlen(buf
), "", 0);
89 hashes
[i
] = hash(method
, i
, entries
[i
]->key
);
92 if (method
& TEST_ADD
) {
93 /* test adding to the map */
94 for (j
= 0; j
< rounds
; j
++) {
95 hashmap_init(&map
, (hashmap_cmp_fn
) test_entry_cmp
, 0);
98 for (i
= 0; i
< TEST_SIZE
; i
++) {
99 hashmap_entry_init(entries
[i
], hashes
[i
]);
100 hashmap_add(&map
, entries
[i
]);
103 hashmap_free(&map
, 0);
106 /* test map lookups */
107 hashmap_init(&map
, (hashmap_cmp_fn
) test_entry_cmp
, 0);
109 /* fill the map (sparsely if specified) */
110 j
= (method
& TEST_SPARSE
) ? TEST_SIZE
/ 10 : TEST_SIZE
;
111 for (i
= 0; i
< j
; i
++) {
112 hashmap_entry_init(entries
[i
], hashes
[i
]);
113 hashmap_add(&map
, entries
[i
]);
116 for (j
= 0; j
< rounds
; j
++) {
117 for (i
= 0; i
< TEST_SIZE
; i
++) {
118 hashmap_get_from_hash(&map
, hashes
[i
],
123 hashmap_free(&map
, 0);
127 #define DELIM " \t\r\n"
130 * Read stdin line by line and print result of commands to stdout:
132 * hash key -> strhash(key) memhash(key) strihash(key) memihash(key)
133 * put key value -> NULL / old value
134 * get key -> NULL / value
135 * remove key -> NULL / old value
136 * iterate -> key1 value1\nkey2 value2\n...
137 * size -> tablesize numentries
139 * perfhashmap method rounds -> test hashmap.[ch] performance
141 int main(int argc
, char *argv
[])
148 icase
= argc
> 1 && !strcmp("ignorecase", argv
[1]);
149 hashmap_init(&map
, (hashmap_cmp_fn
) (icase
? test_entry_cmp_icase
150 : test_entry_cmp
), 0);
152 /* process commands from stdin */
153 while (fgets(line
, sizeof(line
), stdin
)) {
154 char *cmd
, *p1
= NULL
, *p2
= NULL
;
155 int l1
= 0, l2
= 0, hash
= 0;
156 struct test_entry
*entry
;
158 /* break line into command and up to two parameters */
159 cmd
= strtok(line
, DELIM
);
160 /* ignore empty lines */
161 if (!cmd
|| *cmd
== '#')
164 p1
= strtok(NULL
, DELIM
);
167 hash
= icase
? strihash(p1
) : strhash(p1
);
168 p2
= strtok(NULL
, DELIM
);
173 if (!strcmp("hash", cmd
) && l1
) {
175 /* print results of different hash functions */
176 printf("%u %u %u %u\n", strhash(p1
), memhash(p1
, l1
),
177 strihash(p1
), memihash(p1
, l1
));
179 } else if (!strcmp("add", cmd
) && l1
&& l2
) {
181 /* create entry with key = p1, value = p2 */
182 entry
= alloc_test_entry(hash
, p1
, l1
, p2
, l2
);
185 hashmap_add(&map
, entry
);
187 } else if (!strcmp("put", cmd
) && l1
&& l2
) {
189 /* create entry with key = p1, value = p2 */
190 entry
= alloc_test_entry(hash
, p1
, l1
, p2
, l2
);
192 /* add / replace entry */
193 entry
= hashmap_put(&map
, entry
);
195 /* print and free replaced entry, if any */
196 puts(entry
? get_value(entry
) : "NULL");
199 } else if (!strcmp("get", cmd
) && l1
) {
201 /* lookup entry in hashmap */
202 entry
= hashmap_get_from_hash(&map
, hash
, p1
);
208 puts(get_value(entry
));
209 entry
= hashmap_get_next(&map
, entry
);
212 } else if (!strcmp("remove", cmd
) && l1
) {
214 /* setup static key */
215 struct hashmap_entry key
;
216 hashmap_entry_init(&key
, hash
);
218 /* remove entry from hashmap */
219 entry
= hashmap_remove(&map
, &key
, p1
);
221 /* print result and free entry*/
222 puts(entry
? get_value(entry
) : "NULL");
225 } else if (!strcmp("iterate", cmd
)) {
227 struct hashmap_iter iter
;
228 hashmap_iter_init(&map
, &iter
);
229 while ((entry
= hashmap_iter_next(&iter
)))
230 printf("%s %s\n", entry
->key
, get_value(entry
));
232 } else if (!strcmp("size", cmd
)) {
234 /* print table sizes */
235 printf("%u %u\n", map
.tablesize
, map
.size
);
237 } else if (!strcmp("intern", cmd
) && l1
) {
239 /* test that strintern works */
240 const char *i1
= strintern(p1
);
241 const char *i2
= strintern(p1
);
243 printf("strintern(%s) returns %s\n", p1
, i1
);
245 printf("strintern(%s) returns input pointer\n", p1
);
247 printf("strintern(%s) != strintern(%s)", i1
, i2
);
251 } else if (!strcmp("perfhashmap", cmd
) && l1
&& l2
) {
253 perf_hashmap(atoi(p1
), atoi(p2
));
257 printf("Unknown command %s\n", cmd
);
262 hashmap_free(&map
, 1);