1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "private-lib-core.h"
26 #include "private-lib-misc-lwsac.h"
27
28 void
lws_list_ptr_insert(lws_list_ptr * head,lws_list_ptr * add,lws_list_ptr_sort_func_t sort_func)29 lws_list_ptr_insert(lws_list_ptr *head, lws_list_ptr *add,
30 lws_list_ptr_sort_func_t sort_func)
31 {
32 while (sort_func && *head) {
33 if (sort_func(add, *head) <= 0)
34 break;
35
36 head = *head;
37 }
38
39 *add = *head;
40 *head = add;
41 }
42
43 size_t
lwsac_align(size_t length)44 lwsac_align(size_t length)
45 {
46 size_t align = sizeof(int *);
47
48 if (length & (align - 1))
49 length += align - (length & (align - 1));
50
51 return length;
52 }
53
54 size_t
lwsac_sizeof(int first)55 lwsac_sizeof(int first)
56 {
57 return sizeof(struct lwsac) + (first ? sizeof(struct lwsac_head) : 0);
58 }
59
60 size_t
lwsac_get_tail_pos(struct lwsac * lac)61 lwsac_get_tail_pos(struct lwsac *lac)
62 {
63 return lac->ofs;
64 }
65
66 struct lwsac *
lwsac_get_next(struct lwsac * lac)67 lwsac_get_next(struct lwsac *lac)
68 {
69 return lac->next;
70 }
71
72 int
lwsac_extend(struct lwsac * head,size_t amount)73 lwsac_extend(struct lwsac *head, size_t amount)
74 {
75 struct lwsac_head *lachead;
76 struct lwsac *bf;
77
78 assert(head);
79 lachead = (struct lwsac_head *)&head[1];
80
81 bf = lachead->curr;
82 assert(bf);
83
84 if (bf->alloc_size - bf->ofs < lwsac_align(amount))
85 return 1;
86
87 /* memset so constant folding never sees uninitialized data */
88
89 memset(((uint8_t *)bf) + bf->ofs, 0, lwsac_align(amount));
90 bf->ofs += lwsac_align(amount);
91
92 return 0;
93 }
94
95 static void *
_lwsac_use(struct lwsac ** head,size_t ensure,size_t chunk_size,char backfill)96 _lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size, char backfill)
97 {
98 struct lwsac_head *lachead = NULL;
99 size_t ofs, alloc, al, hp;
100 struct lwsac *bf = *head;
101
102 if (bf)
103 lachead = (struct lwsac_head *)&bf[1];
104
105 al = lwsac_align(ensure);
106
107 /* backfill into earlier chunks if that is allowed */
108
109 if (backfill)
110 /*
111 * check if anything can take it, from the start
112 */
113 while (bf) {
114 if (bf->alloc_size - bf->ofs >= ensure)
115 goto do_use;
116
117 bf = bf->next;
118 }
119 else {
120 /*
121 * If there's a current chunk, just check if he can take it
122 */
123 if (lachead && lachead->curr) {
124 bf = lachead->curr;
125 if (bf->alloc_size - bf->ofs >= ensure)
126 goto do_use;
127 }
128 }
129
130 /* nothing can currently take it... so we must allocate */
131
132 hp = sizeof(*bf); /* always need the normal header part... */
133 if (!*head)
134 hp += sizeof(struct lwsac_head);
135
136 if (!chunk_size)
137 alloc = LWSAC_CHUNK_SIZE + hp;
138 else
139 alloc = chunk_size + hp;
140
141 /*
142 * If we get asked for something outside our expectation,
143 * increase the allocation to meet it
144 */
145
146 if (al >= alloc - hp)
147 alloc = al + hp;
148
149 lwsl_debug("%s: alloc %d for %d\n", __func__, (int)alloc, (int)ensure);
150 bf = malloc(alloc);
151 if (!bf) {
152 lwsl_err("%s: OOM trying to alloc %llud\n", __func__,
153 (unsigned long long)alloc);
154 return NULL;
155 }
156
157 /*
158 * belabouring the point... ofs is aligned to the platform's
159 * generic struct alignment at the start then
160 */
161 bf->ofs = sizeof(*bf);
162
163 if (!*head) {
164 /*
165 * We are the first, head, entry...
166 */
167 *head = bf;
168 /*
169 * ... allocate for the special head block
170 */
171 bf->ofs += sizeof(*lachead);
172 lachead = (struct lwsac_head *)&bf[1];
173 memset(lachead, 0, sizeof(*lachead));
174 } else
175 if (lachead->curr)
176 lachead->curr->next = bf;
177
178 lachead->curr = bf;
179 bf->head = *head;
180 bf->next = NULL;
181 bf->alloc_size = alloc;
182
183 lachead->total_alloc_size += alloc;
184 lachead->total_blocks++;
185
186 do_use:
187
188 ofs = bf->ofs;
189
190 if (al > ensure)
191 /* zero down the alignment padding part */
192 memset((char *)bf + ofs + ensure, 0, al - ensure);
193
194 bf->ofs += al;
195 if (bf->ofs >= bf->alloc_size)
196 bf->ofs = bf->alloc_size;
197
198 return (char *)bf + ofs;
199 }
200
201 void *
lwsac_use(struct lwsac ** head,size_t ensure,size_t chunk_size)202 lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size)
203 {
204 return _lwsac_use(head, ensure, chunk_size, 0);
205 }
206
207 void *
lwsac_use_backfill(struct lwsac ** head,size_t ensure,size_t chunk_size)208 lwsac_use_backfill(struct lwsac **head, size_t ensure, size_t chunk_size)
209 {
210 return _lwsac_use(head, ensure, chunk_size, 1);
211 }
212
213 uint8_t *
lwsac_scan_extant(struct lwsac * head,uint8_t * find,size_t len,int nul)214 lwsac_scan_extant(struct lwsac *head, uint8_t *find, size_t len, int nul)
215 {
216 while (head) {
217 uint8_t *pos = (uint8_t *)&head[1],
218 *end = ((uint8_t *)head) + head->ofs - len;
219
220 if (head->ofs - sizeof(*head) >= len)
221 while (pos < end) {
222 if (*pos == *find && (!nul || !pos[len]) &&
223 pos[len - 1] == find[len - 1] &&
224 !memcmp(pos, find, len))
225 /* found the blob */
226 return pos;
227 pos++;
228 }
229
230 head = head->next;
231 }
232
233 return NULL;
234 }
235
236 uint64_t
lwsac_total_overhead(struct lwsac * head)237 lwsac_total_overhead(struct lwsac *head)
238 {
239 uint64_t overhead = 0;
240
241 while (head) {
242 overhead += (head->alloc_size - head->ofs) + sizeof(*head);
243
244 head = head->next;
245 }
246
247 return overhead;
248 }
249
250 void *
lwsac_use_zero(struct lwsac ** head,size_t ensure,size_t chunk_size)251 lwsac_use_zero(struct lwsac **head, size_t ensure, size_t chunk_size)
252 {
253 void *p = lwsac_use(head, ensure, chunk_size);
254
255 if (p)
256 memset(p, 0, ensure);
257
258 return p;
259 }
260
261 void
lwsac_free(struct lwsac ** head)262 lwsac_free(struct lwsac **head)
263 {
264 struct lwsac *it = *head;
265
266 *head = NULL;
267 lwsl_debug("%s: head %p\n", __func__, *head);
268
269 while (it) {
270 struct lwsac *tmp = it->next;
271
272 free(it);
273 it = tmp;
274 }
275 }
276
277 void
lwsac_info(struct lwsac * head)278 lwsac_info(struct lwsac *head)
279 {
280 #if _LWS_ENABLED_LOGS & LLL_DEBUG
281 struct lwsac_head *lachead;
282
283 if (!head) {
284 lwsl_debug("%s: empty\n", __func__);
285 return;
286 }
287
288 lachead = (struct lwsac_head *)&head[1];
289
290 lwsl_debug("%s: lac %p: %dKiB in %d blocks\n", __func__, head,
291 (int)(lachead->total_alloc_size >> 10), lachead->total_blocks);
292 #endif
293 }
294
295 uint64_t
lwsac_total_alloc(struct lwsac * head)296 lwsac_total_alloc(struct lwsac *head)
297 {
298 struct lwsac_head *lachead;
299
300 if (!head)
301 return 0;
302
303 lachead = (struct lwsac_head *)&head[1];
304 return lachead->total_alloc_size;
305 }
306
307 void
lwsac_reference(struct lwsac * head)308 lwsac_reference(struct lwsac *head)
309 {
310 struct lwsac_head *lachead = (struct lwsac_head *)&head[1];
311
312 lachead->refcount++;
313 lwsl_debug("%s: head %p: (det %d) refcount -> %d\n",
314 __func__, head, lachead->detached, lachead->refcount);
315 }
316
317 void
lwsac_unreference(struct lwsac ** head)318 lwsac_unreference(struct lwsac **head)
319 {
320 struct lwsac_head *lachead;
321
322 if (!(*head))
323 return;
324
325 lachead = (struct lwsac_head *)&(*head)[1];
326
327 if (!lachead->refcount)
328 lwsl_warn("%s: refcount going below zero\n", __func__);
329
330 lachead->refcount--;
331
332 lwsl_debug("%s: head %p: (det %d) refcount -> %d\n",
333 __func__, *head, lachead->detached, lachead->refcount);
334
335 if (lachead->detached && !lachead->refcount) {
336 lwsl_debug("%s: head %p: FREED\n", __func__, *head);
337 lwsac_free(head);
338 }
339 }
340
341 void
lwsac_detach(struct lwsac ** head)342 lwsac_detach(struct lwsac **head)
343 {
344 struct lwsac_head *lachead;
345
346 if (!(*head))
347 return;
348
349 lachead = (struct lwsac_head *)&(*head)[1];
350
351 lachead->detached = 1;
352 if (!lachead->refcount) {
353 lwsl_debug("%s: head %p: FREED\n", __func__, *head);
354 lwsac_free(head);
355 } else
356 lwsl_debug("%s: head %p: refcount %d: Marked as detached\n",
357 __func__, *head, lachead->refcount);
358 }
359