1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
4 * Tests for libbpf's hashmap.
6 * Copyright (c) 2019 Facebook
8 #include "test_progs.h"
9 #include "bpf/hashmap.h"
12 static int duration = 0;
14 static size_t hash_fn(long k, void *ctx)
19 static bool equal_fn(long a, long b, void *ctx)
24 static inline size_t next_pow_2(size_t n)
33 static inline size_t exp_cap(size_t sz)
35 size_t r = next_pow_2(sz);
44 static void test_hashmap_generic(void)
46 struct hashmap_entry *entry, *tmp;
47 int err, bkt, found_cnt, i;
51 map = hashmap__new(hash_fn, equal_fn, NULL);
52 if (!ASSERT_OK_PTR(map, "hashmap__new"))
55 for (i = 0; i < ELEM_CNT; i++) {
57 long oldv, v = 1024 + i;
59 err = hashmap__update(map, k, v, &oldk, &oldv);
60 if (CHECK(err != -ENOENT, "hashmap__update",
61 "unexpected result: %d\n", err))
65 err = hashmap__add(map, k, v);
67 err = hashmap__set(map, k, v, &oldk, &oldv);
68 if (CHECK(oldk != 0 || oldv != 0, "check_kv",
69 "unexpected k/v: %ld=%ld\n", oldk, oldv))
73 if (CHECK(err, "elem_add", "failed to add k/v %ld = %ld: %d\n", k, v, err))
76 if (CHECK(!hashmap__find(map, k, &oldv), "elem_find",
77 "failed to find key %ld\n", k))
79 if (CHECK(oldv != v, "elem_val", "found value is wrong: %ld\n", oldv))
83 if (CHECK(hashmap__size(map) != ELEM_CNT, "hashmap__size",
84 "invalid map size: %zu\n", hashmap__size(map)))
86 if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
88 "unexpected map capacity: %zu\n", hashmap__capacity(map)))
92 hashmap__for_each_entry(map, entry, bkt) {
94 long v = entry->value;
96 found_msk |= 1ULL << k;
97 if (CHECK(v - k != 1024, "check_kv",
98 "invalid k/v pair: %ld = %ld\n", k, v))
101 if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1, "elem_cnt",
102 "not all keys iterated: %llx\n", found_msk))
105 for (i = 0; i < ELEM_CNT; i++) {
107 long oldv, v = 256 + i;
109 err = hashmap__add(map, k, v);
110 if (CHECK(err != -EEXIST, "hashmap__add",
111 "unexpected add result: %d\n", err))
115 err = hashmap__update(map, k, v, &oldk, &oldv);
117 err = hashmap__set(map, k, v, &oldk, &oldv);
119 if (CHECK(err, "elem_upd",
120 "failed to update k/v %ld = %ld: %d\n",
123 if (CHECK(!hashmap__find(map, k, &oldv), "elem_find",
124 "failed to find key %ld\n", k))
126 if (CHECK(oldv != v, "elem_val",
127 "found value is wrong: %ld\n", oldv))
131 if (CHECK(hashmap__size(map) != ELEM_CNT, "hashmap__size",
132 "invalid updated map size: %zu\n", hashmap__size(map)))
134 if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
136 "unexpected map capacity: %zu\n", hashmap__capacity(map)))
140 hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
142 long v = entry->value;
144 found_msk |= 1ULL << k;
145 if (CHECK(v - k != 256, "elem_check",
146 "invalid updated k/v pair: %ld = %ld\n", k, v))
149 if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1, "elem_cnt",
150 "not all keys iterated after update: %llx\n", found_msk))
154 hashmap__for_each_key_entry(map, entry, 0) {
157 if (CHECK(!found_cnt, "found_cnt",
158 "didn't find any entries for key 0\n"))
163 hashmap__for_each_key_entry_safe(map, entry, tmp, 0) {
171 found_msk |= 1ULL << k;
173 if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del",
174 "failed to delete k/v %ld = %ld\n", k, v))
176 if (CHECK(oldk != k || oldv != v, "check_old",
177 "invalid deleted k/v: expected %ld = %ld, got %ld = %ld\n",
180 if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del",
181 "unexpectedly deleted k/v %ld = %ld\n", oldk, oldv))
185 if (CHECK(!found_cnt || !found_msk, "found_entries",
186 "didn't delete any key entries\n"))
188 if (CHECK(hashmap__size(map) != ELEM_CNT - found_cnt, "elem_cnt",
189 "invalid updated map size (already deleted: %d): %zu\n",
190 found_cnt, hashmap__size(map)))
192 if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
194 "unexpected map capacity: %zu\n", hashmap__capacity(map)))
197 hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
205 found_msk |= 1ULL << k;
207 if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del",
208 "failed to delete k/v %ld = %ld\n", k, v))
210 if (CHECK(oldk != k || oldv != v, "elem_check",
211 "invalid old k/v: expect %ld = %ld, got %ld = %ld\n",
214 if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del",
215 "unexpectedly deleted k/v %ld = %ld\n", k, v))
219 if (CHECK(found_cnt != ELEM_CNT || found_msk != (1ULL << ELEM_CNT) - 1,
221 "not all keys were deleted: found_cnt:%d, found_msk:%llx\n",
222 found_cnt, found_msk))
224 if (CHECK(hashmap__size(map) != 0, "hashmap__size",
225 "invalid updated map size (already deleted: %d): %zu\n",
226 found_cnt, hashmap__size(map)))
230 hashmap__for_each_entry(map, entry, bkt) {
231 CHECK(false, "elem_exists",
232 "unexpected map entries left: %ld = %ld\n",
233 entry->key, entry->value);
238 hashmap__for_each_entry(map, entry, bkt) {
239 CHECK(false, "elem_exists",
240 "unexpected map entries left: %ld = %ld\n",
241 entry->key, entry->value);
249 static size_t str_hash_fn(long a, void *ctx)
251 return str_hash((char *)a);
254 static bool str_equal_fn(long a, long b, void *ctx)
256 return strcmp((char *)a, (char *)b) == 0;
259 /* Verify that hashmap interface works with pointer keys and values */
260 static void test_hashmap_ptr_iface(void)
262 const char *key, *value, *old_key, *old_value;
263 struct hashmap_entry *cur;
267 map = hashmap__new(str_hash_fn, str_equal_fn, NULL);
268 if (CHECK(!map, "hashmap__new", "can't allocate hashmap\n"))
271 #define CHECK_STR(fn, var, expected) \
272 CHECK(strcmp(var, (expected)), (fn), \
273 "wrong value of " #var ": '%s' instead of '%s'\n", var, (expected))
275 err = hashmap__insert(map, "a", "apricot", HASHMAP_ADD, NULL, NULL);
276 if (CHECK(err, "hashmap__insert", "unexpected error: %d\n", err))
279 err = hashmap__insert(map, "a", "apple", HASHMAP_SET, &old_key, &old_value);
280 if (CHECK(err, "hashmap__insert", "unexpected error: %d\n", err))
282 CHECK_STR("hashmap__update", old_key, "a");
283 CHECK_STR("hashmap__update", old_value, "apricot");
285 err = hashmap__add(map, "b", "banana");
286 if (CHECK(err, "hashmap__add", "unexpected error: %d\n", err))
289 err = hashmap__set(map, "b", "breadfruit", &old_key, &old_value);
290 if (CHECK(err, "hashmap__set", "unexpected error: %d\n", err))
292 CHECK_STR("hashmap__set", old_key, "b");
293 CHECK_STR("hashmap__set", old_value, "banana");
295 err = hashmap__update(map, "b", "blueberry", &old_key, &old_value);
296 if (CHECK(err, "hashmap__update", "unexpected error: %d\n", err))
298 CHECK_STR("hashmap__update", old_key, "b");
299 CHECK_STR("hashmap__update", old_value, "breadfruit");
301 err = hashmap__append(map, "c", "cherry");
302 if (CHECK(err, "hashmap__append", "unexpected error: %d\n", err))
305 if (CHECK(!hashmap__delete(map, "c", &old_key, &old_value),
306 "hashmap__delete", "expected to have entry for 'c'\n"))
308 CHECK_STR("hashmap__delete", old_key, "c");
309 CHECK_STR("hashmap__delete", old_value, "cherry");
311 CHECK(!hashmap__find(map, "b", &value), "hashmap__find", "can't find value for 'b'\n");
312 CHECK_STR("hashmap__find", value, "blueberry");
314 if (CHECK(!hashmap__delete(map, "b", NULL, NULL),
315 "hashmap__delete", "expected to have entry for 'b'\n"))
319 hashmap__for_each_entry(map, cur, bkt) {
320 if (CHECK(i != 0, "hashmap__for_each_entry", "too many entries"))
324 CHECK_STR("entry", key, "a");
325 CHECK_STR("entry", value, "apple");
334 static size_t collision_hash_fn(long k, void *ctx)
339 static void test_hashmap_multimap(void)
342 struct hashmap_entry *entry;
347 /* force collisions */
348 map = hashmap__new(collision_hash_fn, equal_fn, NULL);
349 if (!ASSERT_OK_PTR(map, "hashmap__new"))
356 err = hashmap__append(map, k1, 1);
357 if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
359 err = hashmap__append(map, k1, 2);
360 if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
362 err = hashmap__append(map, k1, 4);
363 if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
366 err = hashmap__append(map, k2, 8);
367 if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
369 err = hashmap__append(map, k2, 16);
370 if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
372 err = hashmap__append(map, k2, 32);
373 if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
376 if (CHECK(hashmap__size(map) != 6, "hashmap_size",
377 "invalid map size: %zu\n", hashmap__size(map)))
380 /* verify global iteration still works and sees all values */
382 hashmap__for_each_entry(map, entry, bkt) {
383 found_msk |= entry->value;
385 if (CHECK(found_msk != (1 << 6) - 1, "found_msk",
386 "not all keys iterated: %lx\n", found_msk))
389 /* iterate values for key 1 */
391 hashmap__for_each_key_entry(map, entry, k1) {
392 found_msk |= entry->value;
394 if (CHECK(found_msk != (1 | 2 | 4), "found_msk",
395 "invalid k1 values: %lx\n", found_msk))
398 /* iterate values for key 2 */
400 hashmap__for_each_key_entry(map, entry, k2) {
401 found_msk |= entry->value;
403 if (CHECK(found_msk != (8 | 16 | 32), "found_msk",
404 "invalid k2 values: %lx\n", found_msk))
411 static void test_hashmap_empty()
413 struct hashmap_entry *entry;
418 /* force collisions */
419 map = hashmap__new(hash_fn, equal_fn, NULL);
420 if (!ASSERT_OK_PTR(map, "hashmap__new"))
423 if (CHECK(hashmap__size(map) != 0, "hashmap__size",
424 "invalid map size: %zu\n", hashmap__size(map)))
426 if (CHECK(hashmap__capacity(map) != 0, "hashmap__capacity",
427 "invalid map capacity: %zu\n", hashmap__capacity(map)))
429 if (CHECK(hashmap__find(map, k, NULL), "elem_find",
430 "unexpected find\n"))
432 if (CHECK(hashmap__delete(map, k, NULL, NULL), "elem_del",
433 "unexpected delete\n"))
436 hashmap__for_each_entry(map, entry, bkt) {
437 CHECK(false, "elem_found", "unexpected iterated entry\n");
440 hashmap__for_each_key_entry(map, entry, k) {
441 CHECK(false, "key_found", "unexpected key entry\n");
451 if (test__start_subtest("generic"))
452 test_hashmap_generic();
453 if (test__start_subtest("multimap"))
454 test_hashmap_multimap();
455 if (test__start_subtest("empty"))
456 test_hashmap_empty();
457 if (test__start_subtest("ptr_iface"))
458 test_hashmap_ptr_iface();