11a95404fSEmilio G. Cota /*
21a95404fSEmilio G. Cota * Copyright (C) 2016, Emilio G. Cota <cota@braap.org>
31a95404fSEmilio G. Cota *
41a95404fSEmilio G. Cota * License: GNU GPL, version 2 or later.
51a95404fSEmilio G. Cota * See the COPYING file in the top-level directory.
61a95404fSEmilio G. Cota */
71a95404fSEmilio G. Cota #include "qemu/osdep.h"
81a95404fSEmilio G. Cota #include "qemu/qht.h"
99c7d64ebSEmilio G. Cota #include "qemu/rcu.h"
101a95404fSEmilio G. Cota
111a95404fSEmilio G. Cota #define N 5000
121a95404fSEmilio G. Cota
131a95404fSEmilio G. Cota static struct qht ht;
141a95404fSEmilio G. Cota static int32_t arr[N * 2];
151a95404fSEmilio G. Cota
is_equal(const void * ap,const void * bp)1661b8cef1SEmilio G. Cota static bool is_equal(const void *ap, const void *bp)
171a95404fSEmilio G. Cota {
1861b8cef1SEmilio G. Cota const int32_t *a = ap;
1961b8cef1SEmilio G. Cota const int32_t *b = bp;
201a95404fSEmilio G. Cota
211a95404fSEmilio G. Cota return *a == *b;
221a95404fSEmilio G. Cota }
231a95404fSEmilio G. Cota
insert(int a,int b)241a95404fSEmilio G. Cota static void insert(int a, int b)
251a95404fSEmilio G. Cota {
261a95404fSEmilio G. Cota int i;
271a95404fSEmilio G. Cota
281a95404fSEmilio G. Cota for (i = a; i < b; i++) {
291a95404fSEmilio G. Cota uint32_t hash;
3032359d52SEmilio G. Cota void *existing;
3132359d52SEmilio G. Cota bool inserted;
321a95404fSEmilio G. Cota
331a95404fSEmilio G. Cota arr[i] = i;
341a95404fSEmilio G. Cota hash = i;
351a95404fSEmilio G. Cota
3632359d52SEmilio G. Cota inserted = qht_insert(&ht, &arr[i], hash, NULL);
3732359d52SEmilio G. Cota g_assert_true(inserted);
3832359d52SEmilio G. Cota inserted = qht_insert(&ht, &arr[i], hash, &existing);
3932359d52SEmilio G. Cota g_assert_false(inserted);
4032359d52SEmilio G. Cota g_assert_true(existing == &arr[i]);
411a95404fSEmilio G. Cota }
421a95404fSEmilio G. Cota }
431a95404fSEmilio G. Cota
do_rm(int init,int end,bool exist)44f44641bbSEmilio G. Cota static void do_rm(int init, int end, bool exist)
451a95404fSEmilio G. Cota {
461a95404fSEmilio G. Cota int i;
471a95404fSEmilio G. Cota
481a95404fSEmilio G. Cota for (i = init; i < end; i++) {
491a95404fSEmilio G. Cota uint32_t hash;
501a95404fSEmilio G. Cota
511a95404fSEmilio G. Cota hash = arr[i];
52f44641bbSEmilio G. Cota if (exist) {
531a95404fSEmilio G. Cota g_assert_true(qht_remove(&ht, &arr[i], hash));
54f44641bbSEmilio G. Cota } else {
55f44641bbSEmilio G. Cota g_assert_false(qht_remove(&ht, &arr[i], hash));
561a95404fSEmilio G. Cota }
571a95404fSEmilio G. Cota }
58f44641bbSEmilio G. Cota }
59f44641bbSEmilio G. Cota
rm(int init,int end)60f44641bbSEmilio G. Cota static void rm(int init, int end)
61f44641bbSEmilio G. Cota {
62f44641bbSEmilio G. Cota do_rm(init, end, true);
63f44641bbSEmilio G. Cota }
64f44641bbSEmilio G. Cota
rm_nonexist(int init,int end)65f44641bbSEmilio G. Cota static void rm_nonexist(int init, int end)
66f44641bbSEmilio G. Cota {
67f44641bbSEmilio G. Cota do_rm(init, end, false);
68f44641bbSEmilio G. Cota }
691a95404fSEmilio G. Cota
check(int a,int b,bool expected)701a95404fSEmilio G. Cota static void check(int a, int b, bool expected)
711a95404fSEmilio G. Cota {
721a95404fSEmilio G. Cota struct qht_stats stats;
731a95404fSEmilio G. Cota int i;
741a95404fSEmilio G. Cota
759c7d64ebSEmilio G. Cota rcu_read_lock();
761a95404fSEmilio G. Cota for (i = a; i < b; i++) {
771a95404fSEmilio G. Cota void *p;
781a95404fSEmilio G. Cota uint32_t hash;
791a95404fSEmilio G. Cota int32_t val;
801a95404fSEmilio G. Cota
811a95404fSEmilio G. Cota val = i;
821a95404fSEmilio G. Cota hash = i;
8361b8cef1SEmilio G. Cota /* test both lookup variants; results should be the same */
8461b8cef1SEmilio G. Cota if (i % 2) {
8561b8cef1SEmilio G. Cota p = qht_lookup(&ht, &val, hash);
8661b8cef1SEmilio G. Cota } else {
8761b8cef1SEmilio G. Cota p = qht_lookup_custom(&ht, &val, hash, is_equal);
8861b8cef1SEmilio G. Cota }
891a95404fSEmilio G. Cota g_assert_true(!!p == expected);
901a95404fSEmilio G. Cota }
919c7d64ebSEmilio G. Cota rcu_read_unlock();
929c7d64ebSEmilio G. Cota
931a95404fSEmilio G. Cota qht_statistics_init(&ht, &stats);
941a95404fSEmilio G. Cota if (stats.used_head_buckets) {
951a95404fSEmilio G. Cota g_assert_cmpfloat(qdist_avg(&stats.chain), >=, 1.0);
961a95404fSEmilio G. Cota }
971a95404fSEmilio G. Cota g_assert_cmpuint(stats.head_buckets, >, 0);
981a95404fSEmilio G. Cota qht_statistics_destroy(&stats);
991a95404fSEmilio G. Cota }
1001a95404fSEmilio G. Cota
count_func(void * p,uint32_t hash,void * userp)101*78255ba2SEmilio G. Cota static void count_func(void *p, uint32_t hash, void *userp)
1021a95404fSEmilio G. Cota {
1031a95404fSEmilio G. Cota unsigned int *curr = userp;
1041a95404fSEmilio G. Cota
1051a95404fSEmilio G. Cota (*curr)++;
1061a95404fSEmilio G. Cota }
1071a95404fSEmilio G. Cota
check_n(size_t expected)1081a95404fSEmilio G. Cota static void check_n(size_t expected)
1091a95404fSEmilio G. Cota {
1101a95404fSEmilio G. Cota struct qht_stats stats;
1111a95404fSEmilio G. Cota
1121a95404fSEmilio G. Cota qht_statistics_init(&ht, &stats);
1131a95404fSEmilio G. Cota g_assert_cmpuint(stats.entries, ==, expected);
1141a95404fSEmilio G. Cota qht_statistics_destroy(&stats);
1151a95404fSEmilio G. Cota }
1161a95404fSEmilio G. Cota
iter_check(unsigned int count)1171a95404fSEmilio G. Cota static void iter_check(unsigned int count)
1181a95404fSEmilio G. Cota {
1191a95404fSEmilio G. Cota unsigned int curr = 0;
1201a95404fSEmilio G. Cota
1211a95404fSEmilio G. Cota qht_iter(&ht, count_func, &curr);
1221a95404fSEmilio G. Cota g_assert_cmpuint(curr, ==, count);
1231a95404fSEmilio G. Cota }
1241a95404fSEmilio G. Cota
sum_func(void * p,uint32_t hash,void * userp)125*78255ba2SEmilio G. Cota static void sum_func(void *p, uint32_t hash, void *userp)
126922034e7SEmilio G. Cota {
127922034e7SEmilio G. Cota uint32_t *sum = userp;
128922034e7SEmilio G. Cota uint32_t a = *(uint32_t *)p;
129922034e7SEmilio G. Cota
130922034e7SEmilio G. Cota *sum += a;
131922034e7SEmilio G. Cota }
132922034e7SEmilio G. Cota
iter_sum_check(unsigned int expected)133922034e7SEmilio G. Cota static void iter_sum_check(unsigned int expected)
134922034e7SEmilio G. Cota {
135922034e7SEmilio G. Cota unsigned int sum = 0;
136922034e7SEmilio G. Cota
137922034e7SEmilio G. Cota qht_iter(&ht, sum_func, &sum);
138922034e7SEmilio G. Cota g_assert_cmpuint(sum, ==, expected);
139922034e7SEmilio G. Cota }
140922034e7SEmilio G. Cota
rm_mod_func(void * p,uint32_t hash,void * userp)141*78255ba2SEmilio G. Cota static bool rm_mod_func(void *p, uint32_t hash, void *userp)
142922034e7SEmilio G. Cota {
143922034e7SEmilio G. Cota uint32_t a = *(uint32_t *)p;
144922034e7SEmilio G. Cota unsigned int mod = *(unsigned int *)userp;
145922034e7SEmilio G. Cota
146922034e7SEmilio G. Cota return a % mod == 0;
147922034e7SEmilio G. Cota }
148922034e7SEmilio G. Cota
iter_rm_mod(unsigned int mod)149922034e7SEmilio G. Cota static void iter_rm_mod(unsigned int mod)
150922034e7SEmilio G. Cota {
151922034e7SEmilio G. Cota qht_iter_remove(&ht, rm_mod_func, &mod);
152922034e7SEmilio G. Cota }
153922034e7SEmilio G. Cota
iter_rm_mod_check(unsigned int mod)154922034e7SEmilio G. Cota static void iter_rm_mod_check(unsigned int mod)
155922034e7SEmilio G. Cota {
156922034e7SEmilio G. Cota unsigned int expected = 0;
157922034e7SEmilio G. Cota unsigned int i;
158922034e7SEmilio G. Cota
159922034e7SEmilio G. Cota for (i = 0; i < N; i++) {
160922034e7SEmilio G. Cota if (i % mod == 0) {
161922034e7SEmilio G. Cota continue;
162922034e7SEmilio G. Cota }
163922034e7SEmilio G. Cota expected += i;
164922034e7SEmilio G. Cota }
165922034e7SEmilio G. Cota iter_sum_check(expected);
166922034e7SEmilio G. Cota }
167922034e7SEmilio G. Cota
qht_do_test(unsigned int mode,size_t init_entries)1681a95404fSEmilio G. Cota static void qht_do_test(unsigned int mode, size_t init_entries)
1691a95404fSEmilio G. Cota {
1707266ae91SEmilio G. Cota /* under KVM we might fetch stats from an uninitialized qht */
1717266ae91SEmilio G. Cota check_n(0);
1727266ae91SEmilio G. Cota
17361b8cef1SEmilio G. Cota qht_init(&ht, is_equal, 0, mode);
174f44641bbSEmilio G. Cota rm_nonexist(0, 4);
175321a33f5SEmilio G. Cota /*
176321a33f5SEmilio G. Cota * Test that we successfully delete the last element in a bucket.
177321a33f5SEmilio G. Cota * This is a hard-to-reach code path when resizing is on, but without
178321a33f5SEmilio G. Cota * resizing we can easily hit it if init_entries <= 1.
179321a33f5SEmilio G. Cota * Given that the number of elements per bucket can be 4 or 6 depending on
180321a33f5SEmilio G. Cota * the host's pointer size, test the removal of the 4th and 6th elements.
181321a33f5SEmilio G. Cota */
182f44641bbSEmilio G. Cota insert(0, 4);
183f44641bbSEmilio G. Cota rm_nonexist(5, 6);
184321a33f5SEmilio G. Cota rm(3, 4);
185321a33f5SEmilio G. Cota check_n(3);
186321a33f5SEmilio G. Cota insert(3, 6);
187321a33f5SEmilio G. Cota rm(5, 6);
188321a33f5SEmilio G. Cota check_n(5);
189f44641bbSEmilio G. Cota rm_nonexist(7, 8);
190f44641bbSEmilio G. Cota iter_rm_mod(1);
1911a95404fSEmilio G. Cota
192ca8897a4SEmilio G. Cota if (!(mode & QHT_MODE_AUTO_RESIZE)) {
193ca8897a4SEmilio G. Cota qht_resize(&ht, init_entries * 4 + 4);
194ca8897a4SEmilio G. Cota }
195ca8897a4SEmilio G. Cota
1967266ae91SEmilio G. Cota check_n(0);
197f44641bbSEmilio G. Cota rm_nonexist(0, 10);
1981a95404fSEmilio G. Cota insert(0, N);
1991a95404fSEmilio G. Cota check(0, N, true);
2001a95404fSEmilio G. Cota check_n(N);
2011a95404fSEmilio G. Cota check(-N, -1, false);
2021a95404fSEmilio G. Cota iter_check(N);
2031a95404fSEmilio G. Cota
2041a95404fSEmilio G. Cota rm(101, 102);
2051a95404fSEmilio G. Cota check_n(N - 1);
2061a95404fSEmilio G. Cota insert(N, N * 2);
2071a95404fSEmilio G. Cota check_n(N + N - 1);
2081a95404fSEmilio G. Cota rm(N, N * 2);
2091a95404fSEmilio G. Cota check_n(N - 1);
2101a95404fSEmilio G. Cota insert(101, 102);
2111a95404fSEmilio G. Cota check_n(N);
2121a95404fSEmilio G. Cota
2131a95404fSEmilio G. Cota rm(10, 200);
2141a95404fSEmilio G. Cota check_n(N - 190);
2151a95404fSEmilio G. Cota insert(150, 200);
2161a95404fSEmilio G. Cota check_n(N - 190 + 50);
2171a95404fSEmilio G. Cota insert(10, 150);
2181a95404fSEmilio G. Cota check_n(N);
2191a95404fSEmilio G. Cota
220922034e7SEmilio G. Cota qht_reset(&ht);
221922034e7SEmilio G. Cota insert(0, N);
222f44641bbSEmilio G. Cota rm_nonexist(N, N + 32);
223922034e7SEmilio G. Cota iter_rm_mod(10);
224922034e7SEmilio G. Cota iter_rm_mod_check(10);
225922034e7SEmilio G. Cota check_n(N * 9 / 10);
2261a95404fSEmilio G. Cota qht_reset_size(&ht, 0);
2271a95404fSEmilio G. Cota check_n(0);
2281a95404fSEmilio G. Cota check(0, N, false);
2291a95404fSEmilio G. Cota
2301a95404fSEmilio G. Cota qht_destroy(&ht);
2311a95404fSEmilio G. Cota }
2321a95404fSEmilio G. Cota
qht_test(unsigned int mode)2331a95404fSEmilio G. Cota static void qht_test(unsigned int mode)
2341a95404fSEmilio G. Cota {
2351a95404fSEmilio G. Cota qht_do_test(mode, 0);
2361a95404fSEmilio G. Cota qht_do_test(mode, 1);
2371a95404fSEmilio G. Cota qht_do_test(mode, 2);
2381a95404fSEmilio G. Cota qht_do_test(mode, 8);
2391a95404fSEmilio G. Cota qht_do_test(mode, 16);
2401a95404fSEmilio G. Cota qht_do_test(mode, 8192);
2411a95404fSEmilio G. Cota qht_do_test(mode, 16384);
2421a95404fSEmilio G. Cota }
2431a95404fSEmilio G. Cota
test_default(void)2441a95404fSEmilio G. Cota static void test_default(void)
2451a95404fSEmilio G. Cota {
2461a95404fSEmilio G. Cota qht_test(0);
2471a95404fSEmilio G. Cota }
2481a95404fSEmilio G. Cota
test_resize(void)2491a95404fSEmilio G. Cota static void test_resize(void)
2501a95404fSEmilio G. Cota {
2511a95404fSEmilio G. Cota qht_test(QHT_MODE_AUTO_RESIZE);
2521a95404fSEmilio G. Cota }
2531a95404fSEmilio G. Cota
main(int argc,char * argv[])2541a95404fSEmilio G. Cota int main(int argc, char *argv[])
2551a95404fSEmilio G. Cota {
2561a95404fSEmilio G. Cota g_test_init(&argc, &argv, NULL);
2571a95404fSEmilio G. Cota g_test_add_func("/qht/mode/default", test_default);
2581a95404fSEmilio G. Cota g_test_add_func("/qht/mode/resize", test_resize);
2591a95404fSEmilio G. Cota return g_test_run();
2601a95404fSEmilio G. Cota }
261