1/*
2 * lws-api-test-lws_dsh
3 *
4 * Written in 2010-2021 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
12int
13test1(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;
88bail:
89	lws_dsh_destroy(&dsh);
90
91	return 1;
92}
93
94int
95test3(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 3: multiple dsh, umeetable allocation request
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	 * There's just no room for this, we expect it to fail
137	 */
138
139	if (!lws_dsh_alloc_tail(dsh, 0, blob, 5000, NULL, 0)) {
140		lwsl_err("%s: Didn't fail to alloc as expected\n", __func__);
141
142		goto bail2;
143	}
144
145	if (lws_dsh_alloc_tail(dsh2, 0, "hello again", 11, NULL, 0)) {
146		lwsl_err("%s: Failed to alloc 4\n", __func__);
147
148		goto bail2;
149	}
150
151	lws_dsh_destroy(&dsh2);
152	lws_dsh_destroy(&dsh);
153
154	return 0;
155
156bail2:
157	lws_dsh_destroy(&dsh2);
158
159bail:
160	lws_dsh_destroy(&dsh);
161
162	return 1;
163}
164
165int
166test4(void)
167{
168	uint8_t blob[4096];
169	struct lws_dsh *dsh;
170	size_t size;
171	void *a1;
172
173	memset(blob, 0, sizeof(blob));
174
175	/*
176	 * test 4: use up whole free list, then recover and alloc something
177	 *	   else
178	 */
179
180	dsh = lws_dsh_create(NULL, 4096, 2);
181	if (!dsh) {
182		lwsl_err("%s: Failed to create dsh\n", __func__);
183
184		return 1;
185	}
186
187	if (lws_dsh_alloc_tail(dsh, 0, blob, 4000, NULL, 0)) {
188		lwsl_err("%s: Failed to alloc 1\n", __func__);
189
190		goto bail;
191	}
192
193	if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
194		lwsl_err("%s: no head 1\n", __func__);
195
196		goto bail;
197	}
198	if (size != 4000) {
199		lwsl_err("%s: test 1 mismatch\n", __func__);
200
201		goto bail;
202	}
203	lws_dsh_free(&a1);
204
205	if (lws_dsh_alloc_tail(dsh, 0, "some other string", 17, NULL, 0)) {
206		lwsl_err("%s: Failed to alloc 2\n", __func__);
207
208		goto bail;
209	}
210
211	if (lws_dsh_alloc_tail(dsh, 0, "hello again", 11, NULL, 0)) {
212		lwsl_err("%s: Failed to alloc 3\n", __func__);
213
214		goto bail;
215	}
216
217	if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
218		lwsl_err("%s: no head 1\n", __func__);
219
220		goto bail;
221	}
222	if (size != 17 || memcmp(a1, "some other string", 17)) {
223		lwsl_err("%s: test 1 mismatch\n", __func__);
224
225		goto bail;
226	}
227	lws_dsh_free(&a1);
228
229	if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
230		lwsl_err("%s: no head 2\n", __func__);
231
232		goto bail;
233	}
234	if (size != 11 || memcmp(a1, "hello again", 11)) {
235		lwsl_err("%s: test 2 mismatch (%zu)\n", __func__, size);
236
237		goto bail;
238	}
239
240	lws_dsh_free(&a1);
241
242	lws_dsh_destroy(&dsh);
243
244	return 0;
245bail:
246	lws_dsh_destroy(&dsh);
247
248	return 1;
249}
250
251int
252test5(void)
253{
254	struct lws_dsh *dsh;
255	unsigned int budget;
256	uint8_t blob[4096];
257	lws_xos_t xos;
258	size_t size;
259	void *a1;
260
261	memset(blob, 0, sizeof(blob));
262	lws_xos_init(&xos, 0x123456789abcdef0ull);
263
264	budget = (unsigned int)(lws_xos(&xos) % 4000) + 4000;
265
266	lwsl_notice("%s: budget %u\n", __func__, budget);
267
268
269	/*
270	 * test 5: PRNG-based spamming and erratic bidi draining
271	 */
272
273	dsh = lws_dsh_create(NULL, 409600, 2);
274	if (!dsh) {
275		lwsl_err("%s: Failed to create dsh\n", __func__);
276
277		return 1;
278	}
279
280	do {
281
282		if (lws_xos_percent(&xos, 60)) {
283			/* kind 0 is going to try to write */
284
285			size = (size_t)((lws_xos(&xos) & 127) + 1);
286
287			if (!lws_dsh_alloc_tail(dsh, 0, blob, size, NULL, 0))
288				lwsl_notice("%s: kind 0 alloc %d\n", __func__, (int)size);
289		}
290
291		if (lws_xos_percent(&xos, 80)) {
292			/* kind 1 is going to try to write */
293
294			size = (size_t)((lws_xos(&xos) & 127) + 1);
295
296			if (!lws_dsh_alloc_tail(dsh, 1, blob, size, NULL, 0))
297				lwsl_notice("%s: kind 1 alloc %d\n", __func__, (int)size);
298		}
299
300		if (lws_xos_percent(&xos, 40)) {
301			/* kind 0 is going to try to read */
302
303			while (!lws_dsh_get_head(dsh, 0, &a1, &size)) {
304				lwsl_notice("%s: kind 0 read %d\n", __func__, (int)size);
305				lws_dsh_free(&a1);
306			}
307		}
308
309		if (lws_xos_percent(&xos, 30)) {
310			/* kind 1 is going to try to read */
311
312			while (!lws_dsh_get_head(dsh, 1, &a1, &size)) {
313				lwsl_notice("%s: kind 1 read %d\n", __func__, (int)size);
314				lws_dsh_free(&a1);
315			}
316		}
317
318	} while (budget--);
319
320	while (!lws_dsh_get_head(dsh, 0, &a1, &size)) {
321		lwsl_notice("%s: kind 0 read %d\n", __func__, (int)size);
322		lws_dsh_free(&a1);
323	}
324
325	while (!lws_dsh_get_head(dsh, 1, &a1, &size)) {
326		lwsl_notice("%s: kind 1 read %d\n", __func__, (int)size);
327		lws_dsh_free(&a1);
328	}
329
330#if defined(_DEBUG)
331	lws_dsh_describe(dsh, "test dsh end state");
332#endif
333
334	lws_dsh_destroy(&dsh);
335
336	return 0;
337}
338
339int main(int argc, const char **argv)
340{
341	int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
342	int ret = 0, n;
343	const char *p;
344
345	if ((p = lws_cmdline_option(argc, argv, "-d")))
346		logs = atoi(p);
347
348	lws_set_log_level(logs, NULL);
349	lwsl_user("LWS API selftest: lws_dsh\n");
350
351	n = test1();
352	lwsl_user("%s: test1: %d\n", __func__, n);
353	ret |= n;
354
355	n = test3();
356	lwsl_user("%s: test3: %d\n", __func__, n);
357	ret |= n;
358
359	n = test4();
360	lwsl_user("%s: test4: %d\n", __func__, n);
361	ret |= n;
362
363	n = test5();
364	lwsl_user("%s: test5: %d\n", __func__, n);
365	ret |= n;
366
367	lwsl_user("Completed: %s\n", ret ? "FAIL" : "PASS");
368
369	return ret;
370}
371