Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
7  * Ben Ford <[email protected]>
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 <[email protected]>
25  * \author Ben Ford <[email protected]>
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  */
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 */
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  */
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 */
160  buffer->free_fn = free_fn ? free_fn : free_fn_do_nothing;
161  buffer->max = size;
162 
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) {
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 
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) {
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 
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 
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 
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 
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 }
Asterisk main include file. File version handling, generic pbx functions.
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
Data buffer containing fixed number of data payloads.
Definition: data_buffer.c:59
Data Buffer API.
String manipulation functions.
Payload entry placed inside of the data buffer list.
Definition: data_buffer.c:47
void * payload
The payload for this position.
Definition: data_buffer.c:49
#define ast_assert(a)
Definition: utils.h:695
struct ast_data_buffer::@378 payloads
A linked list of data payloads.
#define NULL
Definition: resample.c:96
size_t pos
The provided position for this.
Definition: data_buffer.c:51
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 AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
size_t max
Maximum number of data payloads in the buffer.
Definition: data_buffer.c:69
#define CACHED_PAYLOAD_MAX
The number of payloads to increment the cache by.
Definition: data_buffer.c:42
static void free_fn_do_nothing(void *data)
Definition: data_buffer.c:74
void * ast_data_buffer_remove_head(struct ast_data_buffer *buffer)
Remove the first payload from the data buffer.
Definition: data_buffer.c:320
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
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
A set of macros to manage forward-linked lists.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
AST_LIST_HEAD_NOLOCK(contactliststruct, contact)
void ast_data_buffer_free(struct ast_data_buffer *buffer)
Free a data buffer (and all held data payloads)
Definition: data_buffer.c:338
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
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
size_t cache_count
The current number of data payloads in the cache.
Definition: data_buffer.c:71
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
void ast_data_buffer_resize(struct ast_data_buffer *buffer, size_t size)
Resize a data buffer.
Definition: data_buffer.c:168
struct ast_data_buffer::@379 cached_payloads
A linked list of unused cached data payloads.
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
size_t count
The current number of data payloads in the buffer.
Definition: data_buffer.c:67
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
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
Support for logging to various files, console and syslog Configuration in file logger.conf.
struct data_buffer_payload_entry::@377 list
Linked list information.
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:680
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
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
static void data_buffer_free_buffer_payload(struct ast_data_buffer *buffer, struct data_buffer_payload_entry *buffer_payload)
Definition: data_buffer.c:284
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
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
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:598
ast_data_buffer_free_callback free_fn
Callback function to free a data payload.
Definition: data_buffer.c:61