Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
  2
  3/*
  4 * Tests for libbpf's hashmap.
  5 *
  6 * Copyright (c) 2019 Facebook
  7 */
  8#include "test_progs.h"
  9#include "bpf/hashmap.h"
 10#include <stddef.h>
 11
 12static int duration = 0;
 13
 14static size_t hash_fn(long k, void *ctx)
 15{
 16	return k;
 17}
 18
 19static bool equal_fn(long a, long b, void *ctx)
 20{
 21	return a == b;
 22}
 23
 24static inline size_t next_pow_2(size_t n)
 25{
 26	size_t r = 1;
 27
 28	while (r < n)
 29		r <<= 1;
 30	return r;
 31}
 32
 33static inline size_t exp_cap(size_t sz)
 34{
 35	size_t r = next_pow_2(sz);
 36
 37	if (sz * 4 / 3 > r)
 38		r <<= 1;
 39	return r;
 40}
 41
 42#define ELEM_CNT 62
 43
 44static void test_hashmap_generic(void)
 45{
 46	struct hashmap_entry *entry, *tmp;
 47	int err, bkt, found_cnt, i;
 48	long long found_msk;
 49	struct hashmap *map;
 50
 51	map = hashmap__new(hash_fn, equal_fn, NULL);
 52	if (!ASSERT_OK_PTR(map, "hashmap__new"))
 53		return;
 54
 55	for (i = 0; i < ELEM_CNT; i++) {
 56		long oldk, k = i;
 57		long oldv, v = 1024 + i;
 58
 59		err = hashmap__update(map, k, v, &oldk, &oldv);
 60		if (CHECK(err != -ENOENT, "hashmap__update",
 61			  "unexpected result: %d\n", err))
 62			goto cleanup;
 63
 64		if (i % 2) {
 65			err = hashmap__add(map, k, v);
 66		} else {
 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))
 70				goto cleanup;
 71		}
 72
 73		if (CHECK(err, "elem_add", "failed to add k/v %ld = %ld: %d\n", k, v, err))
 
 74			goto cleanup;
 75
 76		if (CHECK(!hashmap__find(map, k, &oldv), "elem_find",
 77			  "failed to find key %ld\n", k))
 78			goto cleanup;
 79		if (CHECK(oldv != v, "elem_val", "found value is wrong: %ld\n", oldv))
 
 80			goto cleanup;
 81	}
 82
 83	if (CHECK(hashmap__size(map) != ELEM_CNT, "hashmap__size",
 84		  "invalid map size: %zu\n", hashmap__size(map)))
 85		goto cleanup;
 86	if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
 87		  "hashmap_cap",
 88		  "unexpected map capacity: %zu\n", hashmap__capacity(map)))
 89		goto cleanup;
 90
 91	found_msk = 0;
 92	hashmap__for_each_entry(map, entry, bkt) {
 93		long k = entry->key;
 94		long v = entry->value;
 95
 96		found_msk |= 1ULL << k;
 97		if (CHECK(v - k != 1024, "check_kv",
 98			  "invalid k/v pair: %ld = %ld\n", k, v))
 99			goto cleanup;
100	}
101	if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1, "elem_cnt",
102		  "not all keys iterated: %llx\n", found_msk))
103		goto cleanup;
104
105	for (i = 0; i < ELEM_CNT; i++) {
106		long oldk, k = i;
107		long oldv, v = 256 + i;
108
109		err = hashmap__add(map, k, v);
110		if (CHECK(err != -EEXIST, "hashmap__add",
111			  "unexpected add result: %d\n", err))
112			goto cleanup;
113
114		if (i % 2)
115			err = hashmap__update(map, k, v, &oldk, &oldv);
116		else
117			err = hashmap__set(map, k, v, &oldk, &oldv);
118
119		if (CHECK(err, "elem_upd",
120			  "failed to update k/v %ld = %ld: %d\n",
121			  k, v, err))
122			goto cleanup;
123		if (CHECK(!hashmap__find(map, k, &oldv), "elem_find",
124			  "failed to find key %ld\n", k))
125			goto cleanup;
126		if (CHECK(oldv != v, "elem_val",
127			  "found value is wrong: %ld\n", oldv))
128			goto cleanup;
129	}
130
131	if (CHECK(hashmap__size(map) != ELEM_CNT, "hashmap__size",
132		  "invalid updated map size: %zu\n", hashmap__size(map)))
133		goto cleanup;
134	if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
135		  "hashmap__capacity",
136		  "unexpected map capacity: %zu\n", hashmap__capacity(map)))
137		goto cleanup;
138
139	found_msk = 0;
140	hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
141		long k = entry->key;
142		long v = entry->value;
143
144		found_msk |= 1ULL << k;
145		if (CHECK(v - k != 256, "elem_check",
146			  "invalid updated k/v pair: %ld = %ld\n", k, v))
147			goto cleanup;
148	}
149	if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1, "elem_cnt",
150		  "not all keys iterated after update: %llx\n", found_msk))
151		goto cleanup;
152
153	found_cnt = 0;
154	hashmap__for_each_key_entry(map, entry, 0) {
155		found_cnt++;
156	}
157	if (CHECK(!found_cnt, "found_cnt",
158		  "didn't find any entries for key 0\n"))
159		goto cleanup;
160
161	found_msk = 0;
162	found_cnt = 0;
163	hashmap__for_each_key_entry_safe(map, entry, tmp, 0) {
164		long oldk, k;
165		long oldv, v;
166
167		k = entry->key;
168		v = entry->value;
169
170		found_cnt++;
171		found_msk |= 1ULL << k;
172
173		if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del",
174			  "failed to delete k/v %ld = %ld\n", k, v))
 
175			goto cleanup;
176		if (CHECK(oldk != k || oldv != v, "check_old",
177			  "invalid deleted k/v: expected %ld = %ld, got %ld = %ld\n",
178			  k, v, oldk, oldv))
179			goto cleanup;
180		if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del",
181			  "unexpectedly deleted k/v %ld = %ld\n", oldk, oldv))
 
182			goto cleanup;
183	}
184
185	if (CHECK(!found_cnt || !found_msk, "found_entries",
186		  "didn't delete any key entries\n"))
187		goto cleanup;
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)))
191		goto cleanup;
192	if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
193		  "hashmap__capacity",
194		  "unexpected map capacity: %zu\n", hashmap__capacity(map)))
195		goto cleanup;
196
197	hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
198		long oldk, k;
199		long oldv, v;
200
201		k = entry->key;
202		v = entry->value;
203
204		found_cnt++;
205		found_msk |= 1ULL << k;
206
207		if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del",
208			  "failed to delete k/v %ld = %ld\n", k, v))
 
209			goto cleanup;
210		if (CHECK(oldk != k || oldv != v, "elem_check",
211			  "invalid old k/v: expect %ld = %ld, got %ld = %ld\n",
212			  k, v, oldk, oldv))
213			goto cleanup;
214		if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del",
215			  "unexpectedly deleted k/v %ld = %ld\n", k, v))
 
216			goto cleanup;
217	}
218
219	if (CHECK(found_cnt != ELEM_CNT || found_msk != (1ULL << ELEM_CNT) - 1,
220		  "found_cnt",
221		  "not all keys were deleted: found_cnt:%d, found_msk:%llx\n",
222		  found_cnt, found_msk))
223		goto cleanup;
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)))
227		goto cleanup;
228
229	found_cnt = 0;
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);
234		goto cleanup;
235	}
236
237	hashmap__clear(map);
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);
242		goto cleanup;
243	}
244
245cleanup:
246	hashmap__free(map);
247}
248
249static size_t str_hash_fn(long a, void *ctx)
250{
251	return str_hash((char *)a);
252}
253
254static bool str_equal_fn(long a, long b, void *ctx)
255{
256	return strcmp((char *)a, (char *)b) == 0;
257}
258
259/* Verify that hashmap interface works with pointer keys and values */
260static void test_hashmap_ptr_iface(void)
261{
262	const char *key, *value, *old_key, *old_value;
263	struct hashmap_entry *cur;
264	struct hashmap *map;
265	int err, i, bkt;
266
267	map = hashmap__new(str_hash_fn, str_equal_fn, NULL);
268	if (CHECK(!map, "hashmap__new", "can't allocate hashmap\n"))
269		goto cleanup;
270
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))
274
275	err = hashmap__insert(map, "a", "apricot", HASHMAP_ADD, NULL, NULL);
276	if (CHECK(err, "hashmap__insert", "unexpected error: %d\n", err))
277		goto cleanup;
278
279	err = hashmap__insert(map, "a", "apple", HASHMAP_SET, &old_key, &old_value);
280	if (CHECK(err, "hashmap__insert", "unexpected error: %d\n", err))
281		goto cleanup;
282	CHECK_STR("hashmap__update", old_key, "a");
283	CHECK_STR("hashmap__update", old_value, "apricot");
284
285	err = hashmap__add(map, "b", "banana");
286	if (CHECK(err, "hashmap__add", "unexpected error: %d\n", err))
287		goto cleanup;
288
289	err = hashmap__set(map, "b", "breadfruit", &old_key, &old_value);
290	if (CHECK(err, "hashmap__set", "unexpected error: %d\n", err))
291		goto cleanup;
292	CHECK_STR("hashmap__set", old_key, "b");
293	CHECK_STR("hashmap__set", old_value, "banana");
294
295	err = hashmap__update(map, "b", "blueberry", &old_key, &old_value);
296	if (CHECK(err, "hashmap__update", "unexpected error: %d\n", err))
297		goto cleanup;
298	CHECK_STR("hashmap__update", old_key, "b");
299	CHECK_STR("hashmap__update", old_value, "breadfruit");
300
301	err = hashmap__append(map, "c", "cherry");
302	if (CHECK(err, "hashmap__append", "unexpected error: %d\n", err))
303		goto cleanup;
304
305	if (CHECK(!hashmap__delete(map, "c", &old_key, &old_value),
306		  "hashmap__delete", "expected to have entry for 'c'\n"))
307		goto cleanup;
308	CHECK_STR("hashmap__delete", old_key, "c");
309	CHECK_STR("hashmap__delete", old_value, "cherry");
310
311	CHECK(!hashmap__find(map, "b", &value), "hashmap__find", "can't find value for 'b'\n");
312	CHECK_STR("hashmap__find", value, "blueberry");
313
314	if (CHECK(!hashmap__delete(map, "b", NULL, NULL),
315		  "hashmap__delete", "expected to have entry for 'b'\n"))
316		goto cleanup;
317
318	i = 0;
319	hashmap__for_each_entry(map, cur, bkt) {
320		if (CHECK(i != 0, "hashmap__for_each_entry", "too many entries"))
321			goto cleanup;
322		key = cur->pkey;
323		value = cur->pvalue;
324		CHECK_STR("entry", key, "a");
325		CHECK_STR("entry", value, "apple");
326		i++;
327	}
328#undef CHECK_STR
329
330cleanup:
331	hashmap__free(map);
332}
333
334static size_t collision_hash_fn(long k, void *ctx)
335{
336	return 0;
337}
338
339static void test_hashmap_multimap(void)
340{
341	long k1 = 0, k2 = 1;
342	struct hashmap_entry *entry;
343	struct hashmap *map;
344	long found_msk;
345	int err, bkt;
346
347	/* force collisions */
348	map = hashmap__new(collision_hash_fn, equal_fn, NULL);
349	if (!ASSERT_OK_PTR(map, "hashmap__new"))
350		return;
351
352	/* set up multimap:
353	 * [0] -> 1, 2, 4;
354	 * [1] -> 8, 16, 32;
355	 */
356	err = hashmap__append(map, k1, 1);
357	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
358		goto cleanup;
359	err = hashmap__append(map, k1, 2);
360	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
361		goto cleanup;
362	err = hashmap__append(map, k1, 4);
363	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
364		goto cleanup;
365
366	err = hashmap__append(map, k2, 8);
367	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
368		goto cleanup;
369	err = hashmap__append(map, k2, 16);
370	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
371		goto cleanup;
372	err = hashmap__append(map, k2, 32);
373	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
374		goto cleanup;
375
376	if (CHECK(hashmap__size(map) != 6, "hashmap_size",
377		  "invalid map size: %zu\n", hashmap__size(map)))
378		goto cleanup;
379
380	/* verify global iteration still works and sees all values */
381	found_msk = 0;
382	hashmap__for_each_entry(map, entry, bkt) {
383		found_msk |= entry->value;
384	}
385	if (CHECK(found_msk != (1 << 6) - 1, "found_msk",
386		  "not all keys iterated: %lx\n", found_msk))
387		goto cleanup;
388
389	/* iterate values for key 1 */
390	found_msk = 0;
391	hashmap__for_each_key_entry(map, entry, k1) {
392		found_msk |= entry->value;
393	}
394	if (CHECK(found_msk != (1 | 2 | 4), "found_msk",
395		  "invalid k1 values: %lx\n", found_msk))
396		goto cleanup;
397
398	/* iterate values for key 2 */
399	found_msk = 0;
400	hashmap__for_each_key_entry(map, entry, k2) {
401		found_msk |= entry->value;
402	}
403	if (CHECK(found_msk != (8 | 16 | 32), "found_msk",
404		  "invalid k2 values: %lx\n", found_msk))
405		goto cleanup;
406
407cleanup:
408	hashmap__free(map);
409}
410
411static void test_hashmap_empty()
412{
413	struct hashmap_entry *entry;
414	int bkt;
415	struct hashmap *map;
416	long k = 0;
417
418	/* force collisions */
419	map = hashmap__new(hash_fn, equal_fn, NULL);
420	if (!ASSERT_OK_PTR(map, "hashmap__new"))
421		goto cleanup;
422
423	if (CHECK(hashmap__size(map) != 0, "hashmap__size",
424		  "invalid map size: %zu\n", hashmap__size(map)))
425		goto cleanup;
426	if (CHECK(hashmap__capacity(map) != 0, "hashmap__capacity",
427		  "invalid map capacity: %zu\n", hashmap__capacity(map)))
428		goto cleanup;
429	if (CHECK(hashmap__find(map, k, NULL), "elem_find",
430		  "unexpected find\n"))
431		goto cleanup;
432	if (CHECK(hashmap__delete(map, k, NULL, NULL), "elem_del",
433		  "unexpected delete\n"))
434		goto cleanup;
435
436	hashmap__for_each_entry(map, entry, bkt) {
437		CHECK(false, "elem_found", "unexpected iterated entry\n");
438		goto cleanup;
439	}
440	hashmap__for_each_key_entry(map, entry, k) {
441		CHECK(false, "key_found", "unexpected key entry\n");
442		goto cleanup;
443	}
444
445cleanup:
446	hashmap__free(map);
447}
448
449void test_hashmap()
450{
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();
459}
v5.14.15
  1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
  2
  3/*
  4 * Tests for libbpf's hashmap.
  5 *
  6 * Copyright (c) 2019 Facebook
  7 */
  8#include "test_progs.h"
  9#include "bpf/hashmap.h"
 
 10
 11static int duration = 0;
 12
 13static size_t hash_fn(const void *k, void *ctx)
 14{
 15	return (long)k;
 16}
 17
 18static bool equal_fn(const void *a, const void *b, void *ctx)
 19{
 20	return (long)a == (long)b;
 21}
 22
 23static inline size_t next_pow_2(size_t n)
 24{
 25	size_t r = 1;
 26
 27	while (r < n)
 28		r <<= 1;
 29	return r;
 30}
 31
 32static inline size_t exp_cap(size_t sz)
 33{
 34	size_t r = next_pow_2(sz);
 35
 36	if (sz * 4 / 3 > r)
 37		r <<= 1;
 38	return r;
 39}
 40
 41#define ELEM_CNT 62
 42
 43static void test_hashmap_generic(void)
 44{
 45	struct hashmap_entry *entry, *tmp;
 46	int err, bkt, found_cnt, i;
 47	long long found_msk;
 48	struct hashmap *map;
 49
 50	map = hashmap__new(hash_fn, equal_fn, NULL);
 51	if (!ASSERT_OK_PTR(map, "hashmap__new"))
 52		return;
 53
 54	for (i = 0; i < ELEM_CNT; i++) {
 55		const void *oldk, *k = (const void *)(long)i;
 56		void *oldv, *v = (void *)(long)(1024 + i);
 57
 58		err = hashmap__update(map, k, v, &oldk, &oldv);
 59		if (CHECK(err != -ENOENT, "hashmap__update",
 60			  "unexpected result: %d\n", err))
 61			goto cleanup;
 62
 63		if (i % 2) {
 64			err = hashmap__add(map, k, v);
 65		} else {
 66			err = hashmap__set(map, k, v, &oldk, &oldv);
 67			if (CHECK(oldk != NULL || oldv != NULL, "check_kv",
 68				  "unexpected k/v: %p=%p\n", oldk, oldv))
 69				goto cleanup;
 70		}
 71
 72		if (CHECK(err, "elem_add", "failed to add k/v %ld = %ld: %d\n",
 73			       (long)k, (long)v, err))
 74			goto cleanup;
 75
 76		if (CHECK(!hashmap__find(map, k, &oldv), "elem_find",
 77			  "failed to find key %ld\n", (long)k))
 78			goto cleanup;
 79		if (CHECK(oldv != v, "elem_val",
 80			  "found value is wrong: %ld\n", (long)oldv))
 81			goto cleanup;
 82	}
 83
 84	if (CHECK(hashmap__size(map) != ELEM_CNT, "hashmap__size",
 85		  "invalid map size: %zu\n", hashmap__size(map)))
 86		goto cleanup;
 87	if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
 88		  "hashmap_cap",
 89		  "unexpected map capacity: %zu\n", hashmap__capacity(map)))
 90		goto cleanup;
 91
 92	found_msk = 0;
 93	hashmap__for_each_entry(map, entry, bkt) {
 94		long k = (long)entry->key;
 95		long v = (long)entry->value;
 96
 97		found_msk |= 1ULL << k;
 98		if (CHECK(v - k != 1024, "check_kv",
 99			  "invalid k/v pair: %ld = %ld\n", k, v))
100			goto cleanup;
101	}
102	if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1, "elem_cnt",
103		  "not all keys iterated: %llx\n", found_msk))
104		goto cleanup;
105
106	for (i = 0; i < ELEM_CNT; i++) {
107		const void *oldk, *k = (const void *)(long)i;
108		void *oldv, *v = (void *)(long)(256 + i);
109
110		err = hashmap__add(map, k, v);
111		if (CHECK(err != -EEXIST, "hashmap__add",
112			  "unexpected add result: %d\n", err))
113			goto cleanup;
114
115		if (i % 2)
116			err = hashmap__update(map, k, v, &oldk, &oldv);
117		else
118			err = hashmap__set(map, k, v, &oldk, &oldv);
119
120		if (CHECK(err, "elem_upd",
121			  "failed to update k/v %ld = %ld: %d\n",
122			  (long)k, (long)v, err))
123			goto cleanup;
124		if (CHECK(!hashmap__find(map, k, &oldv), "elem_find",
125			  "failed to find key %ld\n", (long)k))
126			goto cleanup;
127		if (CHECK(oldv != v, "elem_val",
128			  "found value is wrong: %ld\n", (long)oldv))
129			goto cleanup;
130	}
131
132	if (CHECK(hashmap__size(map) != ELEM_CNT, "hashmap__size",
133		  "invalid updated map size: %zu\n", hashmap__size(map)))
134		goto cleanup;
135	if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
136		  "hashmap__capacity",
137		  "unexpected map capacity: %zu\n", hashmap__capacity(map)))
138		goto cleanup;
139
140	found_msk = 0;
141	hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
142		long k = (long)entry->key;
143		long v = (long)entry->value;
144
145		found_msk |= 1ULL << k;
146		if (CHECK(v - k != 256, "elem_check",
147			  "invalid updated k/v pair: %ld = %ld\n", k, v))
148			goto cleanup;
149	}
150	if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1, "elem_cnt",
151		  "not all keys iterated after update: %llx\n", found_msk))
152		goto cleanup;
153
154	found_cnt = 0;
155	hashmap__for_each_key_entry(map, entry, (void *)0) {
156		found_cnt++;
157	}
158	if (CHECK(!found_cnt, "found_cnt",
159		  "didn't find any entries for key 0\n"))
160		goto cleanup;
161
162	found_msk = 0;
163	found_cnt = 0;
164	hashmap__for_each_key_entry_safe(map, entry, tmp, (void *)0) {
165		const void *oldk, *k;
166		void *oldv, *v;
167
168		k = entry->key;
169		v = entry->value;
170
171		found_cnt++;
172		found_msk |= 1ULL << (long)k;
173
174		if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del",
175			  "failed to delete k/v %ld = %ld\n",
176			  (long)k, (long)v))
177			goto cleanup;
178		if (CHECK(oldk != k || oldv != v, "check_old",
179			  "invalid deleted k/v: expected %ld = %ld, got %ld = %ld\n",
180			  (long)k, (long)v, (long)oldk, (long)oldv))
181			goto cleanup;
182		if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del",
183			  "unexpectedly deleted k/v %ld = %ld\n",
184			  (long)oldk, (long)oldv))
185			goto cleanup;
186	}
187
188	if (CHECK(!found_cnt || !found_msk, "found_entries",
189		  "didn't delete any key entries\n"))
190		goto cleanup;
191	if (CHECK(hashmap__size(map) != ELEM_CNT - found_cnt, "elem_cnt",
192		  "invalid updated map size (already deleted: %d): %zu\n",
193		  found_cnt, hashmap__size(map)))
194		goto cleanup;
195	if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
196		  "hashmap__capacity",
197		  "unexpected map capacity: %zu\n", hashmap__capacity(map)))
198		goto cleanup;
199
200	hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
201		const void *oldk, *k;
202		void *oldv, *v;
203
204		k = entry->key;
205		v = entry->value;
206
207		found_cnt++;
208		found_msk |= 1ULL << (long)k;
209
210		if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del",
211			  "failed to delete k/v %ld = %ld\n",
212			  (long)k, (long)v))
213			goto cleanup;
214		if (CHECK(oldk != k || oldv != v, "elem_check",
215			  "invalid old k/v: expect %ld = %ld, got %ld = %ld\n",
216			  (long)k, (long)v, (long)oldk, (long)oldv))
217			goto cleanup;
218		if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del",
219			  "unexpectedly deleted k/v %ld = %ld\n",
220			  (long)k, (long)v))
221			goto cleanup;
222	}
223
224	if (CHECK(found_cnt != ELEM_CNT || found_msk != (1ULL << ELEM_CNT) - 1,
225		  "found_cnt",
226		  "not all keys were deleted: found_cnt:%d, found_msk:%llx\n",
227		  found_cnt, found_msk))
228		goto cleanup;
229	if (CHECK(hashmap__size(map) != 0, "hashmap__size",
230		  "invalid updated map size (already deleted: %d): %zu\n",
231		  found_cnt, hashmap__size(map)))
232		goto cleanup;
233
234	found_cnt = 0;
235	hashmap__for_each_entry(map, entry, bkt) {
236		CHECK(false, "elem_exists",
237		      "unexpected map entries left: %ld = %ld\n",
238		      (long)entry->key, (long)entry->value);
239		goto cleanup;
240	}
241
242	hashmap__clear(map);
243	hashmap__for_each_entry(map, entry, bkt) {
244		CHECK(false, "elem_exists",
245		      "unexpected map entries left: %ld = %ld\n",
246		      (long)entry->key, (long)entry->value);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247		goto cleanup;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248	}
 
249
250cleanup:
251	hashmap__free(map);
252}
253
254static size_t collision_hash_fn(const void *k, void *ctx)
255{
256	return 0;
257}
258
259static void test_hashmap_multimap(void)
260{
261	void *k1 = (void *)0, *k2 = (void *)1;
262	struct hashmap_entry *entry;
263	struct hashmap *map;
264	long found_msk;
265	int err, bkt;
266
267	/* force collisions */
268	map = hashmap__new(collision_hash_fn, equal_fn, NULL);
269	if (!ASSERT_OK_PTR(map, "hashmap__new"))
270		return;
271
272	/* set up multimap:
273	 * [0] -> 1, 2, 4;
274	 * [1] -> 8, 16, 32;
275	 */
276	err = hashmap__append(map, k1, (void *)1);
277	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
278		goto cleanup;
279	err = hashmap__append(map, k1, (void *)2);
280	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
281		goto cleanup;
282	err = hashmap__append(map, k1, (void *)4);
283	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
284		goto cleanup;
285
286	err = hashmap__append(map, k2, (void *)8);
287	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
288		goto cleanup;
289	err = hashmap__append(map, k2, (void *)16);
290	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
291		goto cleanup;
292	err = hashmap__append(map, k2, (void *)32);
293	if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
294		goto cleanup;
295
296	if (CHECK(hashmap__size(map) != 6, "hashmap_size",
297		  "invalid map size: %zu\n", hashmap__size(map)))
298		goto cleanup;
299
300	/* verify global iteration still works and sees all values */
301	found_msk = 0;
302	hashmap__for_each_entry(map, entry, bkt) {
303		found_msk |= (long)entry->value;
304	}
305	if (CHECK(found_msk != (1 << 6) - 1, "found_msk",
306		  "not all keys iterated: %lx\n", found_msk))
307		goto cleanup;
308
309	/* iterate values for key 1 */
310	found_msk = 0;
311	hashmap__for_each_key_entry(map, entry, k1) {
312		found_msk |= (long)entry->value;
313	}
314	if (CHECK(found_msk != (1 | 2 | 4), "found_msk",
315		  "invalid k1 values: %lx\n", found_msk))
316		goto cleanup;
317
318	/* iterate values for key 2 */
319	found_msk = 0;
320	hashmap__for_each_key_entry(map, entry, k2) {
321		found_msk |= (long)entry->value;
322	}
323	if (CHECK(found_msk != (8 | 16 | 32), "found_msk",
324		  "invalid k2 values: %lx\n", found_msk))
325		goto cleanup;
326
327cleanup:
328	hashmap__free(map);
329}
330
331static void test_hashmap_empty()
332{
333	struct hashmap_entry *entry;
334	int bkt;
335	struct hashmap *map;
336	void *k = (void *)0;
337
338	/* force collisions */
339	map = hashmap__new(hash_fn, equal_fn, NULL);
340	if (!ASSERT_OK_PTR(map, "hashmap__new"))
341		goto cleanup;
342
343	if (CHECK(hashmap__size(map) != 0, "hashmap__size",
344		  "invalid map size: %zu\n", hashmap__size(map)))
345		goto cleanup;
346	if (CHECK(hashmap__capacity(map) != 0, "hashmap__capacity",
347		  "invalid map capacity: %zu\n", hashmap__capacity(map)))
348		goto cleanup;
349	if (CHECK(hashmap__find(map, k, NULL), "elem_find",
350		  "unexpected find\n"))
351		goto cleanup;
352	if (CHECK(hashmap__delete(map, k, NULL, NULL), "elem_del",
353		  "unexpected delete\n"))
354		goto cleanup;
355
356	hashmap__for_each_entry(map, entry, bkt) {
357		CHECK(false, "elem_found", "unexpected iterated entry\n");
358		goto cleanup;
359	}
360	hashmap__for_each_key_entry(map, entry, k) {
361		CHECK(false, "key_found", "unexpected key entry\n");
362		goto cleanup;
363	}
364
365cleanup:
366	hashmap__free(map);
367}
368
369void test_hashmap()
370{
371	if (test__start_subtest("generic"))
372		test_hashmap_generic();
373	if (test__start_subtest("multimap"))
374		test_hashmap_multimap();
375	if (test__start_subtest("empty"))
376		test_hashmap_empty();
 
 
377}