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"
11 static int duration
= 0;
13 static size_t hash_fn(const void *k
, void *ctx
)
18 static bool equal_fn(const void *a
, const void *b
, void *ctx
)
20 return (long)a
== (long)b
;
23 static inline size_t next_pow_2(size_t n
)
32 static inline size_t exp_cap(size_t sz
)
34 size_t r
= next_pow_2(sz
);
43 static void test_hashmap_generic(void)
45 struct hashmap_entry
*entry
, *tmp
;
46 int err
, bkt
, found_cnt
, i
;
50 map
= hashmap__new(hash_fn
, equal_fn
, NULL
);
51 if (CHECK(IS_ERR(map
), "hashmap__new",
52 "failed to create map: %ld\n", PTR_ERR(map
)))
55 for (i
= 0; i
< ELEM_CNT
; i
++) {
56 const void *oldk
, *k
= (const void *)(long)i
;
57 void *oldv
, *v
= (void *)(long)(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
!= NULL
|| oldv
!= NULL
, "check_kv",
69 "unexpected k/v: %p=%p\n", oldk
, oldv
))
73 if (CHECK(err
, "elem_add", "failed to add k/v %ld = %ld: %d\n",
74 (long)k
, (long)v
, err
))
77 if (CHECK(!hashmap__find(map
, k
, &oldv
), "elem_find",
78 "failed to find key %ld\n", (long)k
))
80 if (CHECK(oldv
!= v
, "elem_val",
81 "found value is wrong: %ld\n", (long)oldv
))
85 if (CHECK(hashmap__size(map
) != ELEM_CNT
, "hashmap__size",
86 "invalid map size: %zu\n", hashmap__size(map
)))
88 if (CHECK(hashmap__capacity(map
) != exp_cap(hashmap__size(map
)),
90 "unexpected map capacity: %zu\n", hashmap__capacity(map
)))
94 hashmap__for_each_entry(map
, entry
, bkt
) {
95 long k
= (long)entry
->key
;
96 long v
= (long)entry
->value
;
98 found_msk
|= 1ULL << k
;
99 if (CHECK(v
- k
!= 1024, "check_kv",
100 "invalid k/v pair: %ld = %ld\n", k
, v
))
103 if (CHECK(found_msk
!= (1ULL << ELEM_CNT
) - 1, "elem_cnt",
104 "not all keys iterated: %llx\n", found_msk
))
107 for (i
= 0; i
< ELEM_CNT
; i
++) {
108 const void *oldk
, *k
= (const void *)(long)i
;
109 void *oldv
, *v
= (void *)(long)(256 + i
);
111 err
= hashmap__add(map
, k
, v
);
112 if (CHECK(err
!= -EEXIST
, "hashmap__add",
113 "unexpected add result: %d\n", err
))
117 err
= hashmap__update(map
, k
, v
, &oldk
, &oldv
);
119 err
= hashmap__set(map
, k
, v
, &oldk
, &oldv
);
121 if (CHECK(err
, "elem_upd",
122 "failed to update k/v %ld = %ld: %d\n",
123 (long)k
, (long)v
, err
))
125 if (CHECK(!hashmap__find(map
, k
, &oldv
), "elem_find",
126 "failed to find key %ld\n", (long)k
))
128 if (CHECK(oldv
!= v
, "elem_val",
129 "found value is wrong: %ld\n", (long)oldv
))
133 if (CHECK(hashmap__size(map
) != ELEM_CNT
, "hashmap__size",
134 "invalid updated map size: %zu\n", hashmap__size(map
)))
136 if (CHECK(hashmap__capacity(map
) != exp_cap(hashmap__size(map
)),
138 "unexpected map capacity: %zu\n", hashmap__capacity(map
)))
142 hashmap__for_each_entry_safe(map
, entry
, tmp
, bkt
) {
143 long k
= (long)entry
->key
;
144 long v
= (long)entry
->value
;
146 found_msk
|= 1ULL << k
;
147 if (CHECK(v
- k
!= 256, "elem_check",
148 "invalid updated k/v pair: %ld = %ld\n", k
, v
))
151 if (CHECK(found_msk
!= (1ULL << ELEM_CNT
) - 1, "elem_cnt",
152 "not all keys iterated after update: %llx\n", found_msk
))
156 hashmap__for_each_key_entry(map
, entry
, (void *)0) {
159 if (CHECK(!found_cnt
, "found_cnt",
160 "didn't find any entries for key 0\n"))
165 hashmap__for_each_key_entry_safe(map
, entry
, tmp
, (void *)0) {
166 const void *oldk
, *k
;
173 found_msk
|= 1ULL << (long)k
;
175 if (CHECK(!hashmap__delete(map
, k
, &oldk
, &oldv
), "elem_del",
176 "failed to delete k/v %ld = %ld\n",
179 if (CHECK(oldk
!= k
|| oldv
!= v
, "check_old",
180 "invalid deleted k/v: expected %ld = %ld, got %ld = %ld\n",
181 (long)k
, (long)v
, (long)oldk
, (long)oldv
))
183 if (CHECK(hashmap__delete(map
, k
, &oldk
, &oldv
), "elem_del",
184 "unexpectedly deleted k/v %ld = %ld\n",
185 (long)oldk
, (long)oldv
))
189 if (CHECK(!found_cnt
|| !found_msk
, "found_entries",
190 "didn't delete any key entries\n"))
192 if (CHECK(hashmap__size(map
) != ELEM_CNT
- found_cnt
, "elem_cnt",
193 "invalid updated map size (already deleted: %d): %zu\n",
194 found_cnt
, hashmap__size(map
)))
196 if (CHECK(hashmap__capacity(map
) != exp_cap(hashmap__size(map
)),
198 "unexpected map capacity: %zu\n", hashmap__capacity(map
)))
201 hashmap__for_each_entry_safe(map
, entry
, tmp
, bkt
) {
202 const void *oldk
, *k
;
209 found_msk
|= 1ULL << (long)k
;
211 if (CHECK(!hashmap__delete(map
, k
, &oldk
, &oldv
), "elem_del",
212 "failed to delete k/v %ld = %ld\n",
215 if (CHECK(oldk
!= k
|| oldv
!= v
, "elem_check",
216 "invalid old k/v: expect %ld = %ld, got %ld = %ld\n",
217 (long)k
, (long)v
, (long)oldk
, (long)oldv
))
219 if (CHECK(hashmap__delete(map
, k
, &oldk
, &oldv
), "elem_del",
220 "unexpectedly deleted k/v %ld = %ld\n",
225 if (CHECK(found_cnt
!= ELEM_CNT
|| found_msk
!= (1ULL << ELEM_CNT
) - 1,
227 "not all keys were deleted: found_cnt:%d, found_msk:%llx\n",
228 found_cnt
, found_msk
))
230 if (CHECK(hashmap__size(map
) != 0, "hashmap__size",
231 "invalid updated map size (already deleted: %d): %zu\n",
232 found_cnt
, hashmap__size(map
)))
236 hashmap__for_each_entry(map
, entry
, bkt
) {
237 CHECK(false, "elem_exists",
238 "unexpected map entries left: %ld = %ld\n",
239 (long)entry
->key
, (long)entry
->value
);
244 hashmap__for_each_entry(map
, entry
, bkt
) {
245 CHECK(false, "elem_exists",
246 "unexpected map entries left: %ld = %ld\n",
247 (long)entry
->key
, (long)entry
->value
);
255 static size_t collision_hash_fn(const void *k
, void *ctx
)
260 static void test_hashmap_multimap(void)
262 void *k1
= (void *)0, *k2
= (void *)1;
263 struct hashmap_entry
*entry
;
268 /* force collisions */
269 map
= hashmap__new(collision_hash_fn
, equal_fn
, NULL
);
270 if (CHECK(IS_ERR(map
), "hashmap__new",
271 "failed to create map: %ld\n", PTR_ERR(map
)))
278 err
= hashmap__append(map
, k1
, (void *)1);
279 if (CHECK(err
, "elem_add", "failed to add k/v: %d\n", err
))
281 err
= hashmap__append(map
, k1
, (void *)2);
282 if (CHECK(err
, "elem_add", "failed to add k/v: %d\n", err
))
284 err
= hashmap__append(map
, k1
, (void *)4);
285 if (CHECK(err
, "elem_add", "failed to add k/v: %d\n", err
))
288 err
= hashmap__append(map
, k2
, (void *)8);
289 if (CHECK(err
, "elem_add", "failed to add k/v: %d\n", err
))
291 err
= hashmap__append(map
, k2
, (void *)16);
292 if (CHECK(err
, "elem_add", "failed to add k/v: %d\n", err
))
294 err
= hashmap__append(map
, k2
, (void *)32);
295 if (CHECK(err
, "elem_add", "failed to add k/v: %d\n", err
))
298 if (CHECK(hashmap__size(map
) != 6, "hashmap_size",
299 "invalid map size: %zu\n", hashmap__size(map
)))
302 /* verify global iteration still works and sees all values */
304 hashmap__for_each_entry(map
, entry
, bkt
) {
305 found_msk
|= (long)entry
->value
;
307 if (CHECK(found_msk
!= (1 << 6) - 1, "found_msk",
308 "not all keys iterated: %lx\n", found_msk
))
311 /* iterate values for key 1 */
313 hashmap__for_each_key_entry(map
, entry
, k1
) {
314 found_msk
|= (long)entry
->value
;
316 if (CHECK(found_msk
!= (1 | 2 | 4), "found_msk",
317 "invalid k1 values: %lx\n", found_msk
))
320 /* iterate values for key 2 */
322 hashmap__for_each_key_entry(map
, entry
, k2
) {
323 found_msk
|= (long)entry
->value
;
325 if (CHECK(found_msk
!= (8 | 16 | 32), "found_msk",
326 "invalid k2 values: %lx\n", found_msk
))
333 static void test_hashmap_empty()
335 struct hashmap_entry
*entry
;
340 /* force collisions */
341 map
= hashmap__new(hash_fn
, equal_fn
, NULL
);
342 if (CHECK(IS_ERR(map
), "hashmap__new",
343 "failed to create map: %ld\n", PTR_ERR(map
)))
346 if (CHECK(hashmap__size(map
) != 0, "hashmap__size",
347 "invalid map size: %zu\n", hashmap__size(map
)))
349 if (CHECK(hashmap__capacity(map
) != 0, "hashmap__capacity",
350 "invalid map capacity: %zu\n", hashmap__capacity(map
)))
352 if (CHECK(hashmap__find(map
, k
, NULL
), "elem_find",
353 "unexpected find\n"))
355 if (CHECK(hashmap__delete(map
, k
, NULL
, NULL
), "elem_del",
356 "unexpected delete\n"))
359 hashmap__for_each_entry(map
, entry
, bkt
) {
360 CHECK(false, "elem_found", "unexpected iterated entry\n");
363 hashmap__for_each_key_entry(map
, entry
, k
) {
364 CHECK(false, "key_found", "unexpected key entry\n");
374 if (test__start_subtest("generic"))
375 test_hashmap_generic();
376 if (test__start_subtest("multimap"))
377 test_hashmap_multimap();
378 if (test__start_subtest("empty"))
379 test_hashmap_empty();