1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 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 <libwebsockets.h>
26 #include "private-lib-core.h"
27
28 struct lws_ring *
lws_ring_create(size_t element_len,size_t count,void (* destroy_element)(void *))29 lws_ring_create(size_t element_len, size_t count,
30 void (*destroy_element)(void *))
31 {
32 struct lws_ring *ring = lws_malloc(sizeof(*ring), "ring create");
33
34 if (!ring)
35 return NULL;
36
37 ring->buflen = (uint32_t)(count * element_len);
38 ring->element_len = (uint32_t)element_len;
39 ring->head = 0;
40 ring->oldest_tail = 0;
41 ring->destroy_element = destroy_element;
42
43 ring->buf = lws_malloc(ring->buflen, "ring buf");
44 if (!ring->buf) {
45 lws_free(ring);
46
47 return NULL;
48 }
49
50 return ring;
51 }
52
53 void
lws_ring_destroy(struct lws_ring * ring)54 lws_ring_destroy(struct lws_ring *ring)
55 {
56 if (ring->destroy_element)
57 while (ring->oldest_tail != ring->head) {
58 ring->destroy_element((uint8_t *)ring->buf +
59 ring->oldest_tail);
60 ring->oldest_tail =
61 (ring->oldest_tail + ring->element_len) %
62 ring->buflen;
63 }
64 if (ring->buf)
65 lws_free_set_NULL(ring->buf);
66
67 lws_free(ring);
68 }
69
70 size_t
lws_ring_get_count_free_elements(struct lws_ring * ring)71 lws_ring_get_count_free_elements(struct lws_ring *ring)
72 {
73 int f;
74
75 /*
76 * possible ringbuf patterns
77 *
78 * h == t
79 * |--------t***h---|
80 * |**h-----------t*|
81 * |t**************h|
82 * |*****ht*********|
83 */
84 if (ring->head == ring->oldest_tail)
85 f = ring->buflen - ring->element_len;
86 else
87 if (ring->head < ring->oldest_tail)
88 f = (ring->oldest_tail - ring->head) -
89 ring->element_len;
90 else
91 f = (ring->buflen - ring->head) + ring->oldest_tail -
92 ring->element_len;
93
94 if (f < 2)
95 return 0;
96
97 return f / ring->element_len;
98 }
99
100 size_t
lws_ring_get_count_waiting_elements(struct lws_ring * ring,uint32_t * tail)101 lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail)
102 { int f;
103
104 if (!tail)
105 tail = &ring->oldest_tail;
106 /*
107 * possible ringbuf patterns
108 *
109 * h == t
110 * |--------t***h---|
111 * |**h-----------t*|
112 * |t**************h|
113 * |*****ht*********|
114 */
115 if (ring->head == *tail)
116 f = 0;
117 else
118 if (ring->head > *tail)
119 f = (ring->head - *tail);
120 else
121 f = (ring->buflen - *tail) + ring->head;
122
123 return f / ring->element_len;
124 }
125
126 int
lws_ring_next_linear_insert_range(struct lws_ring * ring,void ** start,size_t * bytes)127 lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start,
128 size_t *bytes)
129 {
130 int n;
131
132 /* n is how many bytes the whole fifo can take */
133 n = (int)(lws_ring_get_count_free_elements(ring) * ring->element_len);
134
135 if (!n)
136 return 1;
137
138 if (ring->head + n > ring->buflen) {
139 *start = (void *)(((uint8_t *)ring->buf) + ring->head);
140 *bytes = ring->buflen - ring->head;
141
142 return 0;
143 }
144
145 *start = (void *)(((uint8_t *)ring->buf) + ring->head);
146 *bytes = n;
147
148 return 0;
149 }
150
151 void
lws_ring_bump_head(struct lws_ring * ring,size_t bytes)152 lws_ring_bump_head(struct lws_ring *ring, size_t bytes)
153 {
154 ring->head = (ring->head + (uint32_t)bytes) % ring->buflen;
155 }
156
157 size_t
lws_ring_insert(struct lws_ring * ring,const void * src,size_t max_count)158 lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count)
159 {
160 const uint8_t *osrc = src;
161 int m, n;
162
163 /* n is how many bytes the whole fifo can take */
164 n = (int)(lws_ring_get_count_free_elements(ring) * ring->element_len);
165
166 /* restrict n to how much we want to insert */
167 if ((uint32_t)n > max_count * ring->element_len)
168 n = (int)(max_count * ring->element_len);
169
170 /*
171 * n is legal to insert, but as an optimization we can cut the
172 * insert into one or two memcpys, depending on if it wraps
173 */
174 if (ring->head + n > ring->buflen) {
175
176 /*
177 * He does wrap. The first memcpy should take us up to
178 * the end of the buffer
179 */
180
181 m = ring->buflen - ring->head;
182 memcpy(((uint8_t *)ring->buf) + ring->head, src, m);
183 /* we know it will wrap exactly back to zero */
184 ring->head = 0;
185
186 /* adapt the second memcpy for what we already did */
187
188 src = ((uint8_t *)src) + m;
189 n -= m;
190 }
191
192 memcpy(((uint8_t *)ring->buf) + ring->head, src, n);
193 ring->head = (ring->head + n) % ring->buflen;
194
195 return (((uint8_t *)src + n) - osrc) / ring->element_len;
196 }
197
198 size_t
lws_ring_consume(struct lws_ring * ring,uint32_t * tail,void * dest,size_t max_count)199 lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest,
200 size_t max_count)
201 {
202 uint8_t *odest = dest;
203 void *orig_tail = tail;
204 uint32_t fake_tail;
205 int m, n;
206
207 if (!tail) {
208 fake_tail = ring->oldest_tail;
209 tail = &fake_tail;
210 }
211
212 /* n is how many bytes the whole fifo has for us */
213 n = (int)(lws_ring_get_count_waiting_elements(ring, tail) *
214 ring->element_len);
215
216 /* restrict n to how much we want to insert */
217 if ((size_t)n > max_count * ring->element_len)
218 n = (int)(max_count * ring->element_len);
219
220 if (!dest) {
221 *tail = ((*tail) + n) % ring->buflen;
222 if (!orig_tail) /* single tail */
223 lws_ring_update_oldest_tail(ring, *tail);
224
225 return n / ring->element_len;
226 }
227 if (*tail + n > ring->buflen) {
228
229 /*
230 * He does wrap. The first memcpy should take us up to
231 * the end of the buffer
232 */
233
234 m = ring->buflen - *tail;
235 memcpy(dest, ((uint8_t *)ring->buf) + *tail, m);
236 /* we know it will wrap exactly back to zero */
237 *tail = 0;
238
239 /* adapt the second memcpy for what we already did */
240
241 dest = ((uint8_t *)dest) + m;
242 n -= m;
243 }
244
245 memcpy(dest, ((uint8_t *)ring->buf) + *tail, n);
246
247 *tail = ((*tail) + n) % ring->buflen;
248 if (!orig_tail) /* single tail */
249 lws_ring_update_oldest_tail(ring, *tail);
250
251 return (((uint8_t *)dest + n) - odest) / ring->element_len;
252 }
253
254 const void *
lws_ring_get_element(struct lws_ring * ring,uint32_t * tail)255 lws_ring_get_element(struct lws_ring *ring, uint32_t *tail)
256 {
257 if (!tail)
258 tail = &ring->oldest_tail;
259
260 if (*tail == ring->head)
261 return NULL;
262
263 return ((uint8_t *)ring->buf) + *tail;
264 }
265
266 void
lws_ring_update_oldest_tail(struct lws_ring * ring,uint32_t tail)267 lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail)
268 {
269 if (!ring->destroy_element) {
270 ring->oldest_tail = tail;
271 return;
272 }
273
274 while (ring->oldest_tail != tail) {
275 ring->destroy_element((uint8_t *)ring->buf + ring->oldest_tail);
276 ring->oldest_tail = (ring->oldest_tail + ring->element_len) %
277 ring->buflen;
278 }
279 }
280
281 uint32_t
lws_ring_get_oldest_tail(struct lws_ring * ring)282 lws_ring_get_oldest_tail(struct lws_ring *ring)
283 {
284 return ring->oldest_tail;
285 }
286
287 void
lws_ring_dump(struct lws_ring * ring,uint32_t * tail)288 lws_ring_dump(struct lws_ring *ring, uint32_t *tail)
289 {
290 if (tail == NULL)
291 tail = &ring->oldest_tail;
292 lwsl_notice("ring %p: buflen %u, elem_len %u, head %u, oldest_tail %u\n"
293 " free_elems: %u; for tail %u, waiting elements: %u\n",
294 ring, (int)ring->buflen, (int)ring->element_len,
295 (int)ring->head, (int)ring->oldest_tail,
296 (int)lws_ring_get_count_free_elements(ring), (int)*tail,
297 (int)lws_ring_get_count_waiting_elements(ring, tail));
298 }
299