1 /*
2 * lws-api-test-lws_dsh
3 *
4 * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5 *
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
8 */
9
10 #include <libwebsockets.h>
11
12 int
test1(void)13 test1(void)
14 {
15 struct lws_dsh *dsh;
16 size_t size;
17 void *a1;
18
19 /*
20 * test 1: single dsh, alloc 2 kinds and free everything back to a
21 * single free obj
22 */
23
24 dsh = lws_dsh_create(NULL, 16384, 2);
25 if (!dsh) {
26 lwsl_err("%s: Failed to create dsh\n", __func__);
27
28 return 1;
29 }
30
31 if (lws_dsh_alloc_tail(dsh, 0, "hello", 5, NULL, 0)) {
32 lwsl_err("%s: Failed to alloc 1\n", __func__);
33
34 goto bail;
35 }
36
37 if (lws_dsh_alloc_tail(dsh, 1, "some other string", 17, NULL, 0)) {
38 lwsl_err("%s: Failed to alloc 2\n", __func__);
39
40 goto bail;
41 }
42
43 if (lws_dsh_alloc_tail(dsh, 0, "hello again", 11, NULL, 0)) {
44 lwsl_err("%s: Failed to alloc 3\n", __func__);
45
46 goto bail;
47 }
48
49 if (lws_dsh_get_head(dsh, 1, &a1, &size)) {
50 lwsl_err("%s: no head 1\n", __func__);
51
52 goto bail;
53 }
54 if (size != 17 || memcmp(a1, "some other string", 17)) {
55 lwsl_err("%s: test 1 mismatch\n", __func__);
56
57 goto bail;
58 }
59 lws_dsh_free(&a1);
60
61 if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
62 lwsl_err("%s: no head 2\n", __func__);
63
64 goto bail;
65 }
66 if (size != 5 || memcmp(a1, "hello", 5)) {
67 lwsl_err("%s: test 2 mismatch\n", __func__);
68
69 goto bail;
70 }
71 lws_dsh_free(&a1);
72
73 if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
74 lwsl_err("%s: no head 3\n", __func__);
75
76 goto bail;
77 }
78 if (size != 11 || memcmp(a1, "hello again", 11)) {
79 lwsl_err("%s: test 3 mismatch\n", __func__);
80
81 goto bail;
82 }
83 lws_dsh_free(&a1);
84
85 lws_dsh_destroy(&dsh);
86
87 return 0;
88 bail:
89 lws_dsh_destroy(&dsh);
90
91 return 1;
92 }
93
94 int
test2(void)95 test2(void)
96 {
97 struct lws_dsh *dsh, *dsh2;
98 lws_dll2_owner_t owner;
99 uint8_t blob[4096];
100
101 memset(blob, 0, sizeof(blob));
102
103 /*
104 * test 2: multiple dsh, overflow allocation and dynamic destroy
105 */
106
107 lws_dll2_owner_clear(&owner);
108
109 dsh = lws_dsh_create(&owner, 4096, 2);
110 if (!dsh) {
111 lwsl_err("%s: Failed to create dsh1\n", __func__);
112
113 return 1;
114 }
115
116 dsh2 = lws_dsh_create(&owner, 4096, 2);
117 if (!dsh2) {
118 lwsl_err("%s: Failed to create dsh2\n", __func__);
119
120 goto bail;
121 }
122
123 if (lws_dsh_alloc_tail(dsh, 0, blob, 4000, NULL, 0)) {
124 lwsl_err("%s: Failed to alloc 1\n", __func__);
125
126 goto bail2;
127 }
128
129 if (lws_dsh_alloc_tail(dsh2, 0, "hello", 5, NULL, 0)) {
130 lwsl_err("%s: Failed to alloc 2\n", __func__);
131
132 goto bail2;
133 }
134
135 /*
136 * We create this logically on dsh. But there's no room for the body.
137 * It should figure out it can use space on dsh2.
138 */
139
140 if (lws_dsh_alloc_tail(dsh, 0, blob, 2000, NULL, 0)) {
141 lwsl_err("%s: Failed to alloc 3\n", __func__);
142
143 goto bail2;
144 }
145
146 if (lws_dsh_alloc_tail(dsh2, 0, "hello again", 11, NULL, 0)) {
147 lwsl_err("%s: Failed to alloc 4\n", __func__);
148
149 goto bail2;
150 }
151
152 /*
153 * When we destroy dsh2 it will try to migrate out the 2000 allocation
154 * from there but find there is no space in dsh1. It should handle it
155 * by logicalling dropping the object.
156 */
157
158 lws_dsh_destroy(&dsh2);
159 lws_dsh_destroy(&dsh);
160
161 return 0;
162
163 bail2:
164 lws_dsh_destroy(&dsh2);
165
166 bail:
167 lws_dsh_destroy(&dsh);
168
169 return 1;
170
171 }
172
173 int
test3(void)174 test3(void)
175 {
176 struct lws_dsh *dsh, *dsh2;
177 lws_dll2_owner_t owner;
178 uint8_t blob[4096];
179
180 memset(blob, 0, sizeof(blob));
181
182 /*
183 * test 3: multiple dsh, umeetable allocation request
184 */
185
186 lws_dll2_owner_clear(&owner);
187
188 dsh = lws_dsh_create(&owner, 4096, 2);
189 if (!dsh) {
190 lwsl_err("%s: Failed to create dsh1\n", __func__);
191
192 return 1;
193 }
194
195 dsh2 = lws_dsh_create(&owner, 4096, 2);
196 if (!dsh2) {
197 lwsl_err("%s: Failed to create dsh2\n", __func__);
198
199 goto bail;
200 }
201
202 if (lws_dsh_alloc_tail(dsh, 0, blob, 4000, NULL, 0)) {
203 lwsl_err("%s: Failed to alloc 1\n", __func__);
204
205 goto bail2;
206 }
207
208 if (lws_dsh_alloc_tail(dsh2, 0, "hello", 5, NULL, 0)) {
209 lwsl_err("%s: Failed to alloc 2\n", __func__);
210
211 goto bail2;
212 }
213
214 /*
215 * There's just no room for this, we expect it to fail
216 */
217
218 if (!lws_dsh_alloc_tail(dsh, 0, blob, 5000, NULL, 0)) {
219 lwsl_err("%s: Didn't fail to alloc as expected\n", __func__);
220
221 goto bail2;
222 }
223
224 if (lws_dsh_alloc_tail(dsh2, 0, "hello again", 11, NULL, 0)) {
225 lwsl_err("%s: Failed to alloc 4\n", __func__);
226
227 goto bail2;
228 }
229
230 lws_dsh_destroy(&dsh2);
231 lws_dsh_destroy(&dsh);
232
233 return 0;
234
235 bail2:
236 lws_dsh_destroy(&dsh2);
237
238 bail:
239 lws_dsh_destroy(&dsh);
240
241 return 1;
242 }
243
244 int
test4(void)245 test4(void)
246 {
247 uint8_t blob[4096];
248 struct lws_dsh *dsh;
249 size_t size;
250 void *a1;
251
252 memset(blob, 0, sizeof(blob));
253
254 /*
255 * test 1: use up whole free list, then recover and alloc something
256 * else
257 */
258
259 dsh = lws_dsh_create(NULL, 4096, 2);
260 if (!dsh) {
261 lwsl_err("%s: Failed to create dsh\n", __func__);
262
263 return 1;
264 }
265
266 if (lws_dsh_alloc_tail(dsh, 0, blob, 4000, NULL, 0)) {
267 lwsl_err("%s: Failed to alloc 1\n", __func__);
268
269 goto bail;
270 }
271
272 if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
273 lwsl_err("%s: no head 1\n", __func__);
274
275 goto bail;
276 }
277 if (size != 4000) {
278 lwsl_err("%s: test 1 mismatch\n", __func__);
279
280 goto bail;
281 }
282 lws_dsh_free(&a1);
283
284 if (lws_dsh_alloc_tail(dsh, 0, "some other string", 17, NULL, 0)) {
285 lwsl_err("%s: Failed to alloc 2\n", __func__);
286
287 goto bail;
288 }
289
290 if (lws_dsh_alloc_tail(dsh, 0, "hello again", 11, NULL, 0)) {
291 lwsl_err("%s: Failed to alloc 3\n", __func__);
292
293 goto bail;
294 }
295
296 if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
297 lwsl_err("%s: no head 1\n", __func__);
298
299 goto bail;
300 }
301 if (size != 17 || memcmp(a1, "some other string", 17)) {
302 lwsl_err("%s: test 1 mismatch\n", __func__);
303
304 goto bail;
305 }
306 lws_dsh_free(&a1);
307
308 if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
309 lwsl_err("%s: no head 2\n", __func__);
310
311 goto bail;
312 }
313 if (size != 11 || memcmp(a1, "hello again", 11)) {
314 lwsl_err("%s: test 2 mismatch (%zu)\n", __func__, size);
315
316 goto bail;
317 }
318
319 lws_dsh_free(&a1);
320
321 lws_dsh_destroy(&dsh);
322
323 return 0;
324 bail:
325 lws_dsh_destroy(&dsh);
326
327 return 1;
328 }
329
main(int argc,const char ** argv)330 int main(int argc, const char **argv)
331 {
332 int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
333 int ret = 0, n;
334 const char *p;
335
336 if ((p = lws_cmdline_option(argc, argv, "-d")))
337 logs = atoi(p);
338
339 lws_set_log_level(logs, NULL);
340 lwsl_user("LWS API selftest: lws_dsh\n");
341
342 n = test1();
343 lwsl_user("%s: test1: %d\n", __func__, n);
344 ret |= n;
345
346 n = test2();
347 lwsl_user("%s: test2: %d\n", __func__, n);
348 ret |= n;
349
350 n = test3();
351 lwsl_user("%s: test3: %d\n", __func__, n);
352 ret |= n;
353
354 n = test4();
355 lwsl_user("%s: test4: %d\n", __func__, n);
356 ret |= n;
357
358 lwsl_user("Completed: %s\n", ret ? "FAIL" : "PASS");
359
360 return ret;
361 }
362