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