1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
4 * Tests for libbpf's hashmap.
6 * Copyright (c) 2019 Facebook
10 #include <linux/err.h>
11 #include "bpf/hashmap.h"
13 #define CHECK(condition, format...) ({ \
14 int __ret = !!(condition); \
16 fprintf(stderr, "%s:%d:FAIL ", __func__, __LINE__); \
17 fprintf(stderr, format); \
22 size_t hash_fn(const void *k
, void *ctx
)
27 bool equal_fn(const void *a
, const void *b
, void *ctx
)
29 return (long)a
== (long)b
;
32 static inline size_t next_pow_2(size_t n
)
41 static inline size_t exp_cap(size_t sz
)
43 size_t r
= next_pow_2(sz
);
52 int test_hashmap_generic(void)
54 struct hashmap_entry
*entry
, *tmp
;
55 int err
, bkt
, found_cnt
, i
;
59 fprintf(stderr
, "%s: ", __func__
);
61 map
= hashmap__new(hash_fn
, equal_fn
, NULL
);
62 if (CHECK(IS_ERR(map
), "failed to create map: %ld\n", PTR_ERR(map
)))
65 for (i
= 0; i
< ELEM_CNT
; i
++) {
66 const void *oldk
, *k
= (const void *)(long)i
;
67 void *oldv
, *v
= (void *)(long)(1024 + i
);
69 err
= hashmap__update(map
, k
, v
, &oldk
, &oldv
);
70 if (CHECK(err
!= -ENOENT
, "unexpected result: %d\n", err
))
74 err
= hashmap__add(map
, k
, v
);
76 err
= hashmap__set(map
, k
, v
, &oldk
, &oldv
);
77 if (CHECK(oldk
!= NULL
|| oldv
!= NULL
,
78 "unexpected k/v: %p=%p\n", oldk
, oldv
))
82 if (CHECK(err
, "failed to add k/v %ld = %ld: %d\n",
83 (long)k
, (long)v
, err
))
86 if (CHECK(!hashmap__find(map
, k
, &oldv
),
87 "failed to find key %ld\n", (long)k
))
89 if (CHECK(oldv
!= v
, "found value is wrong: %ld\n", (long)oldv
))
93 if (CHECK(hashmap__size(map
) != ELEM_CNT
,
94 "invalid map size: %zu\n", hashmap__size(map
)))
96 if (CHECK(hashmap__capacity(map
) != exp_cap(hashmap__size(map
)),
97 "unexpected map capacity: %zu\n", hashmap__capacity(map
)))
101 hashmap__for_each_entry(map
, entry
, bkt
) {
102 long k
= (long)entry
->key
;
103 long v
= (long)entry
->value
;
105 found_msk
|= 1ULL << k
;
106 if (CHECK(v
- k
!= 1024, "invalid k/v pair: %ld = %ld\n", k
, v
))
109 if (CHECK(found_msk
!= (1ULL << ELEM_CNT
) - 1,
110 "not all keys iterated: %llx\n", found_msk
))
113 for (i
= 0; i
< ELEM_CNT
; i
++) {
114 const void *oldk
, *k
= (const void *)(long)i
;
115 void *oldv
, *v
= (void *)(long)(256 + i
);
117 err
= hashmap__add(map
, k
, v
);
118 if (CHECK(err
!= -EEXIST
, "unexpected add result: %d\n", err
))
122 err
= hashmap__update(map
, k
, v
, &oldk
, &oldv
);
124 err
= hashmap__set(map
, k
, v
, &oldk
, &oldv
);
126 if (CHECK(err
, "failed to update k/v %ld = %ld: %d\n",
127 (long)k
, (long)v
, err
))
129 if (CHECK(!hashmap__find(map
, k
, &oldv
),
130 "failed to find key %ld\n", (long)k
))
132 if (CHECK(oldv
!= v
, "found value is wrong: %ld\n", (long)oldv
))
136 if (CHECK(hashmap__size(map
) != ELEM_CNT
,
137 "invalid updated map size: %zu\n", hashmap__size(map
)))
139 if (CHECK(hashmap__capacity(map
) != exp_cap(hashmap__size(map
)),
140 "unexpected map capacity: %zu\n", hashmap__capacity(map
)))
144 hashmap__for_each_entry_safe(map
, entry
, tmp
, bkt
) {
145 long k
= (long)entry
->key
;
146 long v
= (long)entry
->value
;
148 found_msk
|= 1ULL << k
;
149 if (CHECK(v
- k
!= 256,
150 "invalid updated k/v pair: %ld = %ld\n", k
, v
))
153 if (CHECK(found_msk
!= (1ULL << ELEM_CNT
) - 1,
154 "not all keys iterated after update: %llx\n", found_msk
))
158 hashmap__for_each_key_entry(map
, entry
, (void *)0) {
161 if (CHECK(!found_cnt
, "didn't find any entries for key 0\n"))
166 hashmap__for_each_key_entry_safe(map
, entry
, tmp
, (void *)0) {
167 const void *oldk
, *k
;
174 found_msk
|= 1ULL << (long)k
;
176 if (CHECK(!hashmap__delete(map
, k
, &oldk
, &oldv
),
177 "failed to delete k/v %ld = %ld\n",
180 if (CHECK(oldk
!= k
|| oldv
!= v
,
181 "invalid deleted k/v: expected %ld = %ld, got %ld = %ld\n",
182 (long)k
, (long)v
, (long)oldk
, (long)oldv
))
184 if (CHECK(hashmap__delete(map
, k
, &oldk
, &oldv
),
185 "unexpectedly deleted k/v %ld = %ld\n",
186 (long)oldk
, (long)oldv
))
190 if (CHECK(!found_cnt
|| !found_msk
,
191 "didn't delete any key entries\n"))
193 if (CHECK(hashmap__size(map
) != ELEM_CNT
- found_cnt
,
194 "invalid updated map size (already deleted: %d): %zu\n",
195 found_cnt
, hashmap__size(map
)))
197 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
),
212 "failed to delete k/v %ld = %ld\n",
215 if (CHECK(oldk
!= k
|| oldv
!= v
,
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
),
220 "unexpectedly deleted k/v %ld = %ld\n",
225 if (CHECK(found_cnt
!= ELEM_CNT
|| found_msk
!= (1ULL << ELEM_CNT
) - 1,
226 "not all keys were deleted: found_cnt:%d, found_msk:%llx\n",
227 found_cnt
, found_msk
))
229 if (CHECK(hashmap__size(map
) != 0,
230 "invalid updated map size (already deleted: %d): %zu\n",
231 found_cnt
, hashmap__size(map
)))
235 hashmap__for_each_entry(map
, entry
, bkt
) {
236 CHECK(false, "unexpected map entries left: %ld = %ld\n",
237 (long)entry
->key
, (long)entry
->value
);
242 hashmap__for_each_entry(map
, entry
, bkt
) {
243 CHECK(false, "unexpected map entries left: %ld = %ld\n",
244 (long)entry
->key
, (long)entry
->value
);
248 fprintf(stderr
, "OK\n");
252 size_t collision_hash_fn(const void *k
, void *ctx
)
257 int test_hashmap_multimap(void)
259 void *k1
= (void *)0, *k2
= (void *)1;
260 struct hashmap_entry
*entry
;
265 fprintf(stderr
, "%s: ", __func__
);
267 /* force collisions */
268 map
= hashmap__new(collision_hash_fn
, equal_fn
, NULL
);
269 if (CHECK(IS_ERR(map
), "failed to create map: %ld\n", PTR_ERR(map
)))
277 err
= hashmap__append(map
, k1
, (void *)1);
278 if (CHECK(err
, "failed to add k/v: %d\n", err
))
280 err
= hashmap__append(map
, k1
, (void *)2);
281 if (CHECK(err
, "failed to add k/v: %d\n", err
))
283 err
= hashmap__append(map
, k1
, (void *)4);
284 if (CHECK(err
, "failed to add k/v: %d\n", err
))
287 err
= hashmap__append(map
, k2
, (void *)8);
288 if (CHECK(err
, "failed to add k/v: %d\n", err
))
290 err
= hashmap__append(map
, k2
, (void *)16);
291 if (CHECK(err
, "failed to add k/v: %d\n", err
))
293 err
= hashmap__append(map
, k2
, (void *)32);
294 if (CHECK(err
, "failed to add k/v: %d\n", err
))
297 if (CHECK(hashmap__size(map
) != 6,
298 "invalid map size: %zu\n", hashmap__size(map
)))
301 /* verify global iteration still works and sees all values */
303 hashmap__for_each_entry(map
, entry
, bkt
) {
304 found_msk
|= (long)entry
->value
;
306 if (CHECK(found_msk
!= (1 << 6) - 1,
307 "not all keys iterated: %lx\n", found_msk
))
310 /* iterate values for key 1 */
312 hashmap__for_each_key_entry(map
, entry
, k1
) {
313 found_msk
|= (long)entry
->value
;
315 if (CHECK(found_msk
!= (1 | 2 | 4),
316 "invalid k1 values: %lx\n", found_msk
))
319 /* iterate values for key 2 */
321 hashmap__for_each_key_entry(map
, entry
, k2
) {
322 found_msk
|= (long)entry
->value
;
324 if (CHECK(found_msk
!= (8 | 16 | 32),
325 "invalid k2 values: %lx\n", found_msk
))
328 fprintf(stderr
, "OK\n");
332 int test_hashmap_empty()
334 struct hashmap_entry
*entry
;
339 fprintf(stderr
, "%s: ", __func__
);
341 /* force collisions */
342 map
= hashmap__new(hash_fn
, equal_fn
, NULL
);
343 if (CHECK(IS_ERR(map
), "failed to create map: %ld\n", PTR_ERR(map
)))
346 if (CHECK(hashmap__size(map
) != 0,
347 "invalid map size: %zu\n", hashmap__size(map
)))
349 if (CHECK(hashmap__capacity(map
) != 0,
350 "invalid map capacity: %zu\n", hashmap__capacity(map
)))
352 if (CHECK(hashmap__find(map
, k
, NULL
), "unexpected find\n"))
354 if (CHECK(hashmap__delete(map
, k
, NULL
, NULL
), "unexpected delete\n"))
357 hashmap__for_each_entry(map
, entry
, bkt
) {
358 CHECK(false, "unexpected iterated entry\n");
361 hashmap__for_each_key_entry(map
, entry
, k
) {
362 CHECK(false, "unexpected key entry\n");
366 fprintf(stderr
, "OK\n");
370 int main(int argc
, char **argv
)
374 if (test_hashmap_generic())
376 if (test_hashmap_multimap())
378 if (test_hashmap_empty())