Asterisk - The Open Source Telephony Project GIT-master-f36a736
data_buffer.c
Go to the documentation of this file.
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"
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 */
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 */
54};
55
56/*!
57 * \brief Data buffer containing fixed number of data payloads
58 */
60 /*! \brief Callback function to free a data payload */
62 /*! \brief A linked list of data payloads */
64 /*! \brief A linked list of unused cached data 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 */
72};
73
74static void free_fn_do_nothing(void *data)
75{
76 return;
77}
78
79/*!
80 * \brief Helper function to allocate a data payload
81 */
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 */
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
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
158
159 /* If free_fn is NULL, just use free_fn_do_nothing as a default */
161 buffer->max = size;
162
164
165 return buffer;
166}
167
168void 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) {
188 buffer->free_fn(existing_payload->payload);
189 ast_free(existing_payload);
190 buffer->count--;
191 remove--;
192 continue;
193 }
194 break;
195 }
197 }
198
199 buffer->max = size;
201}
202
203int 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);
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) {
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 }
255
256 if (inserted == -1) {
257 return -1;
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
269void *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
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
299void *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
310 data_buffer_free_buffer_payload(buffer, buffer_payload);
311
312 return payload;
313 }
314 }
316
317 return NULL;
318}
319
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
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
356size_t ast_data_buffer_count(const struct ast_data_buffer *buffer)
357{
358 ast_assert(buffer != NULL);
359
360 return buffer->count;
361}
362
363size_t ast_data_buffer_max(const struct ast_data_buffer *buffer)
364{
365 ast_assert(buffer != NULL);
366
367 return buffer->max;
368}
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
static void data_buffer_free_buffer_payload(struct ast_data_buffer *buffer, struct data_buffer_payload_entry *buffer_payload)
Definition: data_buffer.c:284
static void ast_data_buffer_cache_adjust(struct ast_data_buffer *buffer)
Helper function that sets the cache to its maximum number of payloads.
Definition: data_buffer.c:100
void * ast_data_buffer_get(const struct ast_data_buffer *buffer, size_t pos)
Retrieve a data payload from the data buffer.
Definition: data_buffer.c:269
struct ast_data_buffer * ast_data_buffer_alloc(ast_data_buffer_free_callback free_fn, size_t size)
Allocate a data buffer.
Definition: data_buffer.c:145
#define CACHED_PAYLOAD_MAX
The number of payloads to increment the cache by.
Definition: data_buffer.c:42
void * ast_data_buffer_remove_head(struct ast_data_buffer *buffer)
Remove the first payload from the data buffer.
Definition: data_buffer.c:320
static void free_fn_do_nothing(void *data)
Definition: data_buffer.c:74
size_t ast_data_buffer_count(const struct ast_data_buffer *buffer)
Return the number of payloads in a data buffer.
Definition: data_buffer.c:356
void ast_data_buffer_resize(struct ast_data_buffer *buffer, size_t size)
Resize a data buffer.
Definition: data_buffer.c:168
int ast_data_buffer_put(struct ast_data_buffer *buffer, size_t pos, void *payload)
Place a data payload at a position in the data buffer.
Definition: data_buffer.c:203
size_t ast_data_buffer_max(const struct ast_data_buffer *buffer)
Return the maximum number of payloads a data buffer can hold.
Definition: data_buffer.c:363
static struct data_buffer_payload_entry * data_buffer_payload_alloc(void *payload, size_t pos)
Helper function to allocate a data payload.
Definition: data_buffer.c:82
void * ast_data_buffer_remove(struct ast_data_buffer *buffer, size_t pos)
Remove a data payload from the data buffer.
Definition: data_buffer.c:299
void ast_data_buffer_free(struct ast_data_buffer *buffer)
Free a data buffer (and all held data payloads)
Definition: data_buffer.c:338
Data Buffer API.
void(* ast_data_buffer_free_callback)(void *data)
A callback function to free a data payload in a data buffer.
Definition: data_buffer.h:48
Support for logging to various files, console and syslog Configuration in file logger....
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
A set of macros to manage forward-linked lists.
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:599
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define remove
#define NULL
Definition: resample.c:96
String manipulation functions.
Data buffer containing fixed number of data payloads.
Definition: data_buffer.c:59
size_t max
Maximum number of data payloads in the buffer.
Definition: data_buffer.c:69
struct ast_data_buffer::@340 payloads
A linked list of data payloads.
size_t count
The current number of data payloads in the buffer.
Definition: data_buffer.c:67
ast_data_buffer_free_callback free_fn
Callback function to free a data payload.
Definition: data_buffer.c:61
struct ast_data_buffer::@341 cached_payloads
A linked list of unused cached data payloads.
size_t cache_count
The current number of data payloads in the cache.
Definition: data_buffer.c:71
Payload entry placed inside of the data buffer list.
Definition: data_buffer.c:47
size_t pos
The provided position for this.
Definition: data_buffer.c:51
struct data_buffer_payload_entry::@339 list
Linked list information.
void * payload
The payload for this position.
Definition: data_buffer.c:49
#define ast_assert(a)
Definition: utils.h:739