CI: Various updates to buildAsterisk.sh
[asterisk/asterisk.git] / main / data_buffer.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2018, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  * Ben Ford <bford@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*! \file
21  *
22  * \brief Data Buffer API
23  *
24  * \author Joshua Colp <jcolp@digium.com>
25  * \author Ben Ford <bford@digium.com>
26  */
27
28 /*** MODULEINFO
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 #include "asterisk/logger.h"
35 #include "asterisk/strings.h"
36 #include "asterisk/data_buffer.h"
37 #include "asterisk/linkedlists.h"
38
39 /*!
40  * \brief The number of payloads to increment the cache by
41  */
42 #define CACHED_PAYLOAD_MAX 5
43
44 /*!
45  * \brief Payload entry placed inside of the data buffer list
46  */
47 struct data_buffer_payload_entry {
48         /*! \brief The payload for this position */
49         void *payload;
50         /*! \brief The provided position for this */
51         size_t pos;
52         /*! \brief Linked list information */
53         AST_LIST_ENTRY(data_buffer_payload_entry) list;
54 };
55
56 /*!
57  * \brief Data buffer containing fixed number of data payloads
58  */
59 struct ast_data_buffer {
60         /*! \brief Callback function to free a data payload */
61         ast_data_buffer_free_callback free_fn;
62         /*! \brief A linked list of data payloads */
63         AST_LIST_HEAD_NOLOCK(, data_buffer_payload_entry) payloads;
64         /*! \brief A linked list of unused cached data payloads */
65         AST_LIST_HEAD_NOLOCK(, data_buffer_payload_entry) cached_payloads;
66         /*! \brief The current number of data payloads in the buffer */
67         size_t count;
68         /*! \brief Maximum number of data payloads in the buffer */
69         size_t max;
70         /*! \brief The current number of data payloads in the cache */
71         size_t cache_count;
72 };
73
74 static void free_fn_do_nothing(void *data)
75 {
76         return;
77 }
78
79 /*!
80  * \brief Helper function to allocate a data payload
81  */
82 static struct data_buffer_payload_entry *data_buffer_payload_alloc(void *payload, size_t pos)
83 {
84         struct data_buffer_payload_entry *data_payload;
85
86         data_payload = ast_calloc(1, sizeof(*data_payload));
87         if (!data_payload) {
88                 return NULL;
89         }
90
91         data_payload->payload = payload;
92         data_payload->pos = pos;
93
94         return data_payload;
95 }
96
97 /*!
98  * \brief Helper function that sets the cache to its maximum number of payloads
99  */
100 static void ast_data_buffer_cache_adjust(struct ast_data_buffer *buffer)
101 {
102         int buffer_space;
103
104         ast_assert(buffer != NULL);
105
106         buffer_space = buffer->max - buffer->count;
107
108         if (buffer->cache_count == buffer_space) {
109                 return;
110         }
111
112         if (buffer->cache_count < buffer_space) {
113                 /* Add payloads to the cache, if able */
114                 while (buffer->cache_count < CACHED_PAYLOAD_MAX && buffer->cache_count < buffer_space) {
115                         struct data_buffer_payload_entry *buffer_payload;
116
117                         buffer_payload = data_buffer_payload_alloc(NULL, -1);
118                         if (buffer_payload) {
119                                 AST_LIST_INSERT_TAIL(&buffer->cached_payloads, buffer_payload, list);
120                                 buffer->cache_count++;
121                                 continue;
122                         }
123
124                         ast_log(LOG_ERROR, "Failed to allocate memory to the cache.");
125                         break;
126                 }
127         } else if (buffer->cache_count > buffer_space) {
128                 /* Remove payloads from the cache */
129                 while (buffer->cache_count > buffer_space) {
130                         struct data_buffer_payload_entry *buffer_payload;
131
132                         buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list);
133                         if (buffer_payload) {
134                                 ast_free(buffer_payload);
135                                 buffer->cache_count--;
136                                 continue;
137                         }
138
139                         ast_log(LOG_ERROR, "Failed to remove memory from the cache.");
140                         break;
141                 }
142         }
143 }
144
145 struct ast_data_buffer *ast_data_buffer_alloc(ast_data_buffer_free_callback free_fn, size_t size)
146 {
147         struct ast_data_buffer *buffer;
148
149         ast_assert(size != 0);
150
151         buffer = ast_calloc(1, sizeof(*buffer));
152         if (!buffer) {
153                 return NULL;
154         }
155
156         AST_LIST_HEAD_INIT_NOLOCK(&buffer->payloads);
157         AST_LIST_HEAD_INIT_NOLOCK(&buffer->cached_payloads);
158
159         /* If free_fn is NULL, just use free_fn_do_nothing as a default */
160         buffer->free_fn = free_fn ? free_fn : free_fn_do_nothing;
161         buffer->max = size;
162
163         ast_data_buffer_cache_adjust(buffer);
164
165         return buffer;
166 }
167
168 void ast_data_buffer_resize(struct ast_data_buffer *buffer, size_t size)
169 {
170         struct data_buffer_payload_entry *existing_payload;
171
172         ast_assert(buffer != NULL);
173
174         /* The buffer must have at least a size of 1 */
175         ast_assert(size > 0);
176
177         if (buffer->max == size) {
178                 return;
179         }
180
181         /* If the size is decreasing, some payloads will need to be freed */
182         if (buffer->max > size) {
183                 int remove = buffer->max - size;
184
185                 AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, existing_payload, list) {
186                         if (remove) {
187                                 AST_LIST_REMOVE_HEAD(&buffer->payloads, list);
188                                 buffer->free_fn(existing_payload->payload);
189                                 ast_free(existing_payload);
190                                 buffer->count--;
191                                 remove--;
192                                 continue;
193                         }
194                         break;
195                 }
196                 AST_LIST_TRAVERSE_SAFE_END;
197         }
198
199         buffer->max = size;
200         ast_data_buffer_cache_adjust(buffer);
201 }
202
203 int ast_data_buffer_put(struct ast_data_buffer *buffer, size_t pos, void *payload)
204 {
205         struct data_buffer_payload_entry *buffer_payload = NULL;
206         struct data_buffer_payload_entry *existing_payload;
207         int inserted = 0;
208
209         ast_assert(buffer != NULL);
210         ast_assert(payload != NULL);
211
212         /* If the data buffer has reached its maximum size then the head goes away and
213          * we will reuse its buffer payload
214          */
215         if (buffer->count == buffer->max) {
216                 buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list);
217                 buffer->free_fn(buffer_payload->payload);
218                 buffer->count--;
219
220                 /* Update this buffer payload with its new information */
221                 buffer_payload->payload = payload;
222                 buffer_payload->pos = pos;
223         }
224         if (!buffer_payload) {
225                 if (!buffer->cache_count) {
226                         ast_data_buffer_cache_adjust(buffer);
227                 }
228                 buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list);
229                 buffer->cache_count--;
230
231                 /* Update the payload from the cache with its new information */
232                 buffer_payload->payload = payload;
233                 buffer_payload->pos = pos;
234         }
235         if (!buffer_payload) {
236                 return -1;
237         }
238
239         /* Given the position find its ideal spot within the buffer */
240         AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, existing_payload, list) {
241                 /* If it's already in the buffer, drop it */
242                 if (existing_payload->pos == pos) {
243                         ast_debug(3, "Packet with position %zu is already in buffer. Not inserting.\n", pos);
244                         inserted = -1;
245                         break;
246                 }
247
248                 if (existing_payload->pos > pos) {
249                         AST_LIST_INSERT_BEFORE_CURRENT(buffer_payload, list);
250                         inserted = 1;
251                         break;
252                 }
253         }
254         AST_LIST_TRAVERSE_SAFE_END;
255
256         if (inserted == -1) {
257                 return 0;
258         }
259
260         if (!inserted) {
261                 AST_LIST_INSERT_TAIL(&buffer->payloads, buffer_payload, list);
262         }
263
264         buffer->count++;
265
266         return 0;
267 }
268
269 void *ast_data_buffer_get(const struct ast_data_buffer *buffer, size_t pos)
270 {
271         struct data_buffer_payload_entry *buffer_payload;
272
273         ast_assert(buffer != NULL);
274
275         AST_LIST_TRAVERSE(&buffer->payloads, buffer_payload, list) {
276                 if (buffer_payload->pos == pos) {
277                         return buffer_payload->payload;
278                 }
279         }
280
281         return NULL;
282 }
283
284 static void data_buffer_free_buffer_payload(struct ast_data_buffer *buffer,
285         struct data_buffer_payload_entry *buffer_payload)
286 {
287         buffer_payload->payload = NULL;
288         buffer->count--;
289
290         if (buffer->cache_count < CACHED_PAYLOAD_MAX
291                         && buffer->cache_count < (buffer->max - buffer->count)) {
292                 AST_LIST_INSERT_TAIL(&buffer->cached_payloads, buffer_payload, list);
293                 buffer->cache_count++;
294         } else {
295                 ast_free(buffer_payload);
296         }
297 }
298
299 void *ast_data_buffer_remove(struct ast_data_buffer *buffer, size_t pos)
300 {
301         struct data_buffer_payload_entry *buffer_payload;
302
303         ast_assert(buffer != NULL);
304
305         AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, buffer_payload, list) {
306                 if (buffer_payload->pos == pos) {
307                         void *payload = buffer_payload->payload;
308
309                         AST_LIST_REMOVE_CURRENT(list);
310                         data_buffer_free_buffer_payload(buffer, buffer_payload);
311
312                         return payload;
313                 }
314         }
315         AST_LIST_TRAVERSE_SAFE_END;
316
317         return NULL;
318 }
319
320 void *ast_data_buffer_remove_head(struct ast_data_buffer *buffer)
321 {
322         ast_assert(buffer != NULL);
323
324         if (buffer->count > 0) {
325                 struct data_buffer_payload_entry *buffer_payload;
326                 void *payload;
327
328                 buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list);
329                 payload = buffer_payload->payload;
330                 data_buffer_free_buffer_payload(buffer, buffer_payload);
331
332                 return payload;
333         }
334
335         return NULL;
336 }
337
338 void ast_data_buffer_free(struct ast_data_buffer *buffer)
339 {
340         struct data_buffer_payload_entry *buffer_payload;
341
342         ast_assert(buffer != NULL);
343
344         while ((buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list))) {
345                 buffer->free_fn(buffer_payload->payload);
346                 ast_free(buffer_payload);
347         }
348
349         while ((buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list))) {
350                 ast_free(buffer_payload);
351         }
352
353         ast_free(buffer);
354 }
355
356 size_t ast_data_buffer_count(const struct ast_data_buffer *buffer)
357 {
358         ast_assert(buffer != NULL);
359
360         return buffer->count;
361 }
362
363 size_t ast_data_buffer_max(const struct ast_data_buffer *buffer)
364 {
365         ast_assert(buffer != NULL);
366
367         return buffer->max;
368 }