xref: /qemu/tests/unit/test-qht.c (revision da668aa1)
1*da668aa1SThomas Huth /*
2*da668aa1SThomas Huth  * Copyright (C) 2016, Emilio G. Cota <cota@braap.org>
3*da668aa1SThomas Huth  *
4*da668aa1SThomas Huth  * License: GNU GPL, version 2 or later.
5*da668aa1SThomas Huth  *   See the COPYING file in the top-level directory.
6*da668aa1SThomas Huth  */
7*da668aa1SThomas Huth #include "qemu/osdep.h"
8*da668aa1SThomas Huth #include "qemu/qht.h"
9*da668aa1SThomas Huth #include "qemu/rcu.h"
10*da668aa1SThomas Huth 
11*da668aa1SThomas Huth #define N 5000
12*da668aa1SThomas Huth 
13*da668aa1SThomas Huth static struct qht ht;
14*da668aa1SThomas Huth static int32_t arr[N * 2];
15*da668aa1SThomas Huth 
is_equal(const void * ap,const void * bp)16*da668aa1SThomas Huth static bool is_equal(const void *ap, const void *bp)
17*da668aa1SThomas Huth {
18*da668aa1SThomas Huth     const int32_t *a = ap;
19*da668aa1SThomas Huth     const int32_t *b = bp;
20*da668aa1SThomas Huth 
21*da668aa1SThomas Huth     return *a == *b;
22*da668aa1SThomas Huth }
23*da668aa1SThomas Huth 
insert(int a,int b)24*da668aa1SThomas Huth static void insert(int a, int b)
25*da668aa1SThomas Huth {
26*da668aa1SThomas Huth     int i;
27*da668aa1SThomas Huth 
28*da668aa1SThomas Huth     for (i = a; i < b; i++) {
29*da668aa1SThomas Huth         uint32_t hash;
30*da668aa1SThomas Huth         void *existing;
31*da668aa1SThomas Huth         bool inserted;
32*da668aa1SThomas Huth 
33*da668aa1SThomas Huth         arr[i] = i;
34*da668aa1SThomas Huth         hash = i;
35*da668aa1SThomas Huth 
36*da668aa1SThomas Huth         inserted = qht_insert(&ht, &arr[i], hash, NULL);
37*da668aa1SThomas Huth         g_assert_true(inserted);
38*da668aa1SThomas Huth         inserted = qht_insert(&ht, &arr[i], hash, &existing);
39*da668aa1SThomas Huth         g_assert_false(inserted);
40*da668aa1SThomas Huth         g_assert_true(existing == &arr[i]);
41*da668aa1SThomas Huth     }
42*da668aa1SThomas Huth }
43*da668aa1SThomas Huth 
do_rm(int init,int end,bool exist)44*da668aa1SThomas Huth static void do_rm(int init, int end, bool exist)
45*da668aa1SThomas Huth {
46*da668aa1SThomas Huth     int i;
47*da668aa1SThomas Huth 
48*da668aa1SThomas Huth     for (i = init; i < end; i++) {
49*da668aa1SThomas Huth         uint32_t hash;
50*da668aa1SThomas Huth 
51*da668aa1SThomas Huth         hash = arr[i];
52*da668aa1SThomas Huth         if (exist) {
53*da668aa1SThomas Huth             g_assert_true(qht_remove(&ht, &arr[i], hash));
54*da668aa1SThomas Huth         } else {
55*da668aa1SThomas Huth             g_assert_false(qht_remove(&ht, &arr[i], hash));
56*da668aa1SThomas Huth         }
57*da668aa1SThomas Huth     }
58*da668aa1SThomas Huth }
59*da668aa1SThomas Huth 
rm(int init,int end)60*da668aa1SThomas Huth static void rm(int init, int end)
61*da668aa1SThomas Huth {
62*da668aa1SThomas Huth     do_rm(init, end, true);
63*da668aa1SThomas Huth }
64*da668aa1SThomas Huth 
rm_nonexist(int init,int end)65*da668aa1SThomas Huth static void rm_nonexist(int init, int end)
66*da668aa1SThomas Huth {
67*da668aa1SThomas Huth     do_rm(init, end, false);
68*da668aa1SThomas Huth }
69*da668aa1SThomas Huth 
check(int a,int b,bool expected)70*da668aa1SThomas Huth static void check(int a, int b, bool expected)
71*da668aa1SThomas Huth {
72*da668aa1SThomas Huth     struct qht_stats stats;
73*da668aa1SThomas Huth     int i;
74*da668aa1SThomas Huth 
75*da668aa1SThomas Huth     rcu_read_lock();
76*da668aa1SThomas Huth     for (i = a; i < b; i++) {
77*da668aa1SThomas Huth         void *p;
78*da668aa1SThomas Huth         uint32_t hash;
79*da668aa1SThomas Huth         int32_t val;
80*da668aa1SThomas Huth 
81*da668aa1SThomas Huth         val = i;
82*da668aa1SThomas Huth         hash = i;
83*da668aa1SThomas Huth         /* test both lookup variants; results should be the same */
84*da668aa1SThomas Huth         if (i % 2) {
85*da668aa1SThomas Huth             p = qht_lookup(&ht, &val, hash);
86*da668aa1SThomas Huth         } else {
87*da668aa1SThomas Huth             p = qht_lookup_custom(&ht, &val, hash, is_equal);
88*da668aa1SThomas Huth         }
89*da668aa1SThomas Huth         g_assert_true(!!p == expected);
90*da668aa1SThomas Huth     }
91*da668aa1SThomas Huth     rcu_read_unlock();
92*da668aa1SThomas Huth 
93*da668aa1SThomas Huth     qht_statistics_init(&ht, &stats);
94*da668aa1SThomas Huth     if (stats.used_head_buckets) {
95*da668aa1SThomas Huth         g_assert_cmpfloat(qdist_avg(&stats.chain), >=, 1.0);
96*da668aa1SThomas Huth     }
97*da668aa1SThomas Huth     g_assert_cmpuint(stats.head_buckets, >, 0);
98*da668aa1SThomas Huth     qht_statistics_destroy(&stats);
99*da668aa1SThomas Huth }
100*da668aa1SThomas Huth 
count_func(void * p,uint32_t hash,void * userp)101*da668aa1SThomas Huth static void count_func(void *p, uint32_t hash, void *userp)
102*da668aa1SThomas Huth {
103*da668aa1SThomas Huth     unsigned int *curr = userp;
104*da668aa1SThomas Huth 
105*da668aa1SThomas Huth     (*curr)++;
106*da668aa1SThomas Huth }
107*da668aa1SThomas Huth 
check_n(size_t expected)108*da668aa1SThomas Huth static void check_n(size_t expected)
109*da668aa1SThomas Huth {
110*da668aa1SThomas Huth     struct qht_stats stats;
111*da668aa1SThomas Huth 
112*da668aa1SThomas Huth     qht_statistics_init(&ht, &stats);
113*da668aa1SThomas Huth     g_assert_cmpuint(stats.entries, ==, expected);
114*da668aa1SThomas Huth     qht_statistics_destroy(&stats);
115*da668aa1SThomas Huth }
116*da668aa1SThomas Huth 
iter_check(unsigned int count)117*da668aa1SThomas Huth static void iter_check(unsigned int count)
118*da668aa1SThomas Huth {
119*da668aa1SThomas Huth     unsigned int curr = 0;
120*da668aa1SThomas Huth 
121*da668aa1SThomas Huth     qht_iter(&ht, count_func, &curr);
122*da668aa1SThomas Huth     g_assert_cmpuint(curr, ==, count);
123*da668aa1SThomas Huth }
124*da668aa1SThomas Huth 
sum_func(void * p,uint32_t hash,void * userp)125*da668aa1SThomas Huth static void sum_func(void *p, uint32_t hash, void *userp)
126*da668aa1SThomas Huth {
127*da668aa1SThomas Huth     uint32_t *sum = userp;
128*da668aa1SThomas Huth     uint32_t a = *(uint32_t *)p;
129*da668aa1SThomas Huth 
130*da668aa1SThomas Huth     *sum += a;
131*da668aa1SThomas Huth }
132*da668aa1SThomas Huth 
iter_sum_check(unsigned int expected)133*da668aa1SThomas Huth static void iter_sum_check(unsigned int expected)
134*da668aa1SThomas Huth {
135*da668aa1SThomas Huth     unsigned int sum = 0;
136*da668aa1SThomas Huth 
137*da668aa1SThomas Huth     qht_iter(&ht, sum_func, &sum);
138*da668aa1SThomas Huth     g_assert_cmpuint(sum, ==, expected);
139*da668aa1SThomas Huth }
140*da668aa1SThomas Huth 
rm_mod_func(void * p,uint32_t hash,void * userp)141*da668aa1SThomas Huth static bool rm_mod_func(void *p, uint32_t hash, void *userp)
142*da668aa1SThomas Huth {
143*da668aa1SThomas Huth     uint32_t a = *(uint32_t *)p;
144*da668aa1SThomas Huth     unsigned int mod = *(unsigned int *)userp;
145*da668aa1SThomas Huth 
146*da668aa1SThomas Huth     return a % mod == 0;
147*da668aa1SThomas Huth }
148*da668aa1SThomas Huth 
iter_rm_mod(unsigned int mod)149*da668aa1SThomas Huth static void iter_rm_mod(unsigned int mod)
150*da668aa1SThomas Huth {
151*da668aa1SThomas Huth     qht_iter_remove(&ht, rm_mod_func, &mod);
152*da668aa1SThomas Huth }
153*da668aa1SThomas Huth 
iter_rm_mod_check(unsigned int mod)154*da668aa1SThomas Huth static void iter_rm_mod_check(unsigned int mod)
155*da668aa1SThomas Huth {
156*da668aa1SThomas Huth     unsigned int expected = 0;
157*da668aa1SThomas Huth     unsigned int i;
158*da668aa1SThomas Huth 
159*da668aa1SThomas Huth     for (i = 0; i < N; i++) {
160*da668aa1SThomas Huth         if (i % mod == 0) {
161*da668aa1SThomas Huth             continue;
162*da668aa1SThomas Huth         }
163*da668aa1SThomas Huth         expected += i;
164*da668aa1SThomas Huth     }
165*da668aa1SThomas Huth     iter_sum_check(expected);
166*da668aa1SThomas Huth }
167*da668aa1SThomas Huth 
qht_do_test(unsigned int mode,size_t init_entries)168*da668aa1SThomas Huth static void qht_do_test(unsigned int mode, size_t init_entries)
169*da668aa1SThomas Huth {
170*da668aa1SThomas Huth     /* under KVM we might fetch stats from an uninitialized qht */
171*da668aa1SThomas Huth     check_n(0);
172*da668aa1SThomas Huth 
173*da668aa1SThomas Huth     qht_init(&ht, is_equal, 0, mode);
174*da668aa1SThomas Huth     rm_nonexist(0, 4);
175*da668aa1SThomas Huth     /*
176*da668aa1SThomas Huth      * Test that we successfully delete the last element in a bucket.
177*da668aa1SThomas Huth      * This is a hard-to-reach code path when resizing is on, but without
178*da668aa1SThomas Huth      * resizing we can easily hit it if init_entries <= 1.
179*da668aa1SThomas Huth      * Given that the number of elements per bucket can be 4 or 6 depending on
180*da668aa1SThomas Huth      * the host's pointer size, test the removal of the 4th and 6th elements.
181*da668aa1SThomas Huth      */
182*da668aa1SThomas Huth     insert(0, 4);
183*da668aa1SThomas Huth     rm_nonexist(5, 6);
184*da668aa1SThomas Huth     rm(3, 4);
185*da668aa1SThomas Huth     check_n(3);
186*da668aa1SThomas Huth     insert(3, 6);
187*da668aa1SThomas Huth     rm(5, 6);
188*da668aa1SThomas Huth     check_n(5);
189*da668aa1SThomas Huth     rm_nonexist(7, 8);
190*da668aa1SThomas Huth     iter_rm_mod(1);
191*da668aa1SThomas Huth 
192*da668aa1SThomas Huth     if (!(mode & QHT_MODE_AUTO_RESIZE)) {
193*da668aa1SThomas Huth         qht_resize(&ht, init_entries * 4 + 4);
194*da668aa1SThomas Huth     }
195*da668aa1SThomas Huth 
196*da668aa1SThomas Huth     check_n(0);
197*da668aa1SThomas Huth     rm_nonexist(0, 10);
198*da668aa1SThomas Huth     insert(0, N);
199*da668aa1SThomas Huth     check(0, N, true);
200*da668aa1SThomas Huth     check_n(N);
201*da668aa1SThomas Huth     check(-N, -1, false);
202*da668aa1SThomas Huth     iter_check(N);
203*da668aa1SThomas Huth 
204*da668aa1SThomas Huth     rm(101, 102);
205*da668aa1SThomas Huth     check_n(N - 1);
206*da668aa1SThomas Huth     insert(N, N * 2);
207*da668aa1SThomas Huth     check_n(N + N - 1);
208*da668aa1SThomas Huth     rm(N, N * 2);
209*da668aa1SThomas Huth     check_n(N - 1);
210*da668aa1SThomas Huth     insert(101, 102);
211*da668aa1SThomas Huth     check_n(N);
212*da668aa1SThomas Huth 
213*da668aa1SThomas Huth     rm(10, 200);
214*da668aa1SThomas Huth     check_n(N - 190);
215*da668aa1SThomas Huth     insert(150, 200);
216*da668aa1SThomas Huth     check_n(N - 190 + 50);
217*da668aa1SThomas Huth     insert(10, 150);
218*da668aa1SThomas Huth     check_n(N);
219*da668aa1SThomas Huth 
220*da668aa1SThomas Huth     qht_reset(&ht);
221*da668aa1SThomas Huth     insert(0, N);
222*da668aa1SThomas Huth     rm_nonexist(N, N + 32);
223*da668aa1SThomas Huth     iter_rm_mod(10);
224*da668aa1SThomas Huth     iter_rm_mod_check(10);
225*da668aa1SThomas Huth     check_n(N * 9 / 10);
226*da668aa1SThomas Huth     qht_reset_size(&ht, 0);
227*da668aa1SThomas Huth     check_n(0);
228*da668aa1SThomas Huth     check(0, N, false);
229*da668aa1SThomas Huth 
230*da668aa1SThomas Huth     qht_destroy(&ht);
231*da668aa1SThomas Huth }
232*da668aa1SThomas Huth 
qht_test(unsigned int mode)233*da668aa1SThomas Huth static void qht_test(unsigned int mode)
234*da668aa1SThomas Huth {
235*da668aa1SThomas Huth     qht_do_test(mode, 0);
236*da668aa1SThomas Huth     qht_do_test(mode, 1);
237*da668aa1SThomas Huth     qht_do_test(mode, 2);
238*da668aa1SThomas Huth     qht_do_test(mode, 8);
239*da668aa1SThomas Huth     qht_do_test(mode, 16);
240*da668aa1SThomas Huth     qht_do_test(mode, 8192);
241*da668aa1SThomas Huth     qht_do_test(mode, 16384);
242*da668aa1SThomas Huth }
243*da668aa1SThomas Huth 
test_default(void)244*da668aa1SThomas Huth static void test_default(void)
245*da668aa1SThomas Huth {
246*da668aa1SThomas Huth     qht_test(0);
247*da668aa1SThomas Huth }
248*da668aa1SThomas Huth 
test_resize(void)249*da668aa1SThomas Huth static void test_resize(void)
250*da668aa1SThomas Huth {
251*da668aa1SThomas Huth     qht_test(QHT_MODE_AUTO_RESIZE);
252*da668aa1SThomas Huth }
253*da668aa1SThomas Huth 
main(int argc,char * argv[])254*da668aa1SThomas Huth int main(int argc, char *argv[])
255*da668aa1SThomas Huth {
256*da668aa1SThomas Huth     g_test_init(&argc, &argv, NULL);
257*da668aa1SThomas Huth     g_test_add_func("/qht/mode/default", test_default);
258*da668aa1SThomas Huth     g_test_add_func("/qht/mode/resize", test_resize);
259*da668aa1SThomas Huth     return g_test_run();
260*da668aa1SThomas Huth }
261