<p>Benjamin Keith Ford has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/8606">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Add data buffer API to store packets.<br><br>Adds a data buffer with a configurable size that can store different<br>kinds of packets (like RTP packets for retransmission). Given a number<br>it will store a data packet at that position relative to the others.<br>Given a number it will retrieve the given data packet if it is present.<br>This is purposely a storage of arbitrary things so it can be used not<br>just for RTP packets but also Asterisk frames in the future if needed.<br>The API does not internally use a lock, so it will be up to the user of<br>the API to properly protect the data buffer.<br><br>For more information, refer to the wiki page:<br>https://wiki.asterisk.org/wiki/display/AST/WebRTC+User+Experience+Improvements<br><br>Change-Id: Iff13c5d4795d52356959fe2a57360cd57dfade07<br>---<br>A include/asterisk/data_buffer.h<br>A main/data_buffer.c<br>2 files changed, 471 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/06/8606/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/include/asterisk/data_buffer.h b/include/asterisk/data_buffer.h<br>new file mode 100644<br>index 0000000..119cdbf<br>--- /dev/null<br>+++ b/include/asterisk/data_buffer.h<br>@@ -0,0 +1,157 @@<br>+/*<br>+ * Asterisk -- An open source telephony toolkit.<br>+ *<br>+ * Copyright (C) 2018, Digium, Inc.<br>+ *<br>+ * Joshua Colp <jcolp@digium.com><br>+ *<br>+ * See http://www.asterisk.org for more information about<br>+ * the Asterisk project. Please do not directly contact<br>+ * any of the maintainers of this project for assistance;<br>+ * the project provides a web site, mailing lists and IRC<br>+ * channels for your use.<br>+ *<br>+ * This program is free software, distributed under the terms of<br>+ * the GNU General Public License Version 2. See the LICENSE file<br>+ * at the top of the source tree.<br>+ */<br>+<br>+/*!<br>+ * \file<br>+ * \brief Data Buffer API<br>+ *<br>+ * \author Joshua Colp <jcolp@digium.com><br>+ */<br>+<br>+#ifndef _AST_DATA_BUFFER_H_<br>+#define _AST_DATA_BUFFER_H_<br>+<br>+/*!<br>+ * \brief The number of cached payloads a data buffer starts out with<br>+ */<br>+#define CACHED_PAYLOADS_START 2<br>+<br>+/*!<br>+ * \brief A buffer of data payloads.<br>+ */<br>+struct ast_data_buffer;<br>+<br>+/*!<br>+ * \brief A callback function to free a data payload in a data buffer<br>+ *<br>+ * \param The data payload<br>+ */<br>+typedef void (*ast_data_buffer_free_callback)(void *data);<br>+<br>+/*!<br>+ * \brief Allocate a data buffer<br>+ *<br>+ * \param free_fn Callback function to free a data payload<br>+ * \param size The maximum number of data payloads to contain in the data buffer<br>+ *<br>+ * \retval non-NULL success<br>+ * \retval NULL failure<br>+ *<br>+ * \since 15.4.0<br>+ */<br>+struct ast_data_buffer *ast_data_buffer_alloc(ast_data_buffer_free_callback free_fn, size_t size);<br>+<br>+/*!<br>+ * \brief Sets the data buffer's cache to its max payload count<br>+ *<br>+ * \param buffer The data buffer<br>+ *<br>+ * \since 15.4.0<br>+ */<br>+void ast_data_buffer_cache_adjust(struct ast_data_buffer *buffer);<br>+<br>+/*!<br>+ * \brief Resize a data buffer<br>+ *<br>+ * \param buffer The data buffer<br>+ * \param size The new maximum size of the data buffer<br>+ *<br>+ * \note If the data buffer is shrunk any old data paylods will be freed using the configured callback.<br>+ * The data buffer is flexible and can be used for multiple purposes. Therefore it is up to the<br>+ * caller of the function to know whether or not a buffer should have its size changed. Increasing<br>+ * the size of the buffer may make sense in some scenarios, but shrinking should always be handled<br>+ * with caution since data can be lost.<br>+ *<br>+ * \since 15.4.0<br>+ */<br>+void ast_data_buffer_resize(struct ast_data_buffer *buffer, size_t size);<br>+<br>+/*!<br>+ * \brief Place a data payload at a position in the data buffer<br>+ *<br>+ * \param buffer The data buffer<br>+ * \param pos The position of the data payload<br>+ * \param payload The data payload<br>+ *<br>+ * \retval 0 success<br>+ * \retval -1 failure<br>+ *<br>+ * \note It is up to the consumer of this API to ensure proper memory management of data payloads<br>+ *<br>+ * \since 15.4.0<br>+ */<br>+int ast_data_buffer_put(struct ast_data_buffer *buffer, int pos, void *payload);<br>+<br>+/*!<br>+ * \brief Retrieve a data payload from the data buffer<br>+ *<br>+ * \param buffer The data buffer<br>+ * \param pos The position of the data payload (-1 to get the HEAD data payload)<br>+ *<br>+ * \retval non-NULL success<br>+ * \retval NULL failure<br>+ *<br>+ * \note This does not remove the data payload from the data buffer. It will be removed when it is displaced.<br>+ *<br>+ * \since 15.4.0<br>+ */<br>+void *ast_data_buffer_get(const struct ast_data_buffer *buffer, int pos);<br>+<br>+/*!<br>+ * \brief Free a data buffer (and all held data payloads)<br>+ *<br>+ * \param buffer The data buffer<br>+ *<br>+ * \since 15.4.0<br>+ */<br>+void ast_data_buffer_free(struct ast_data_buffer *buffer);<br>+<br>+/*!<br>+ * \brief Return the number of payloads in a data buffer<br>+ *<br>+ * \param buffer The data buffer<br>+ *<br>+ * \retval the number of data payloads<br>+ *<br>+ * \since 15.4.0<br>+ */<br>+size_t ast_data_buffer_count(const struct ast_data_buffer *buffer);<br>+<br>+/*!<br>+ * \brief Return the maximum number of payloads a data buffer can hold<br>+ *<br>+ * \param buffer The data buffer<br>+ *<br>+ * \retval the maximum number of data payloads<br>+ *<br>+ * \since 15.4.0<br>+ */<br>+size_t ast_data_buffer_max(const struct ast_data_buffer *buffer);<br>+<br>+/*!<br>+ * \brief Return the number of cached payloads in a data buffer<br>+ *<br>+ * \param buffer The data buffer<br>+ *<br>+ * \retval the number of cached data payloads<br>+ *<br>+ * \since 15.4.0<br>+ */<br>+size_t ast_data_buffer_cache_count(const struct ast_data_buffer *buffer);<br>+<br>+#endif /* _AST_DATA_BUFFER_H */<br>diff --git a/main/data_buffer.c b/main/data_buffer.c<br>new file mode 100644<br>index 0000000..b06dd4b<br>--- /dev/null<br>+++ b/main/data_buffer.c<br>@@ -0,0 +1,314 @@<br>+/*<br>+ * Asterisk -- An open source telephony toolkit.<br>+ *<br>+ * Copyright (C) 2018, Digium, Inc.<br>+ *<br>+ * Joshua Colp <jcolp@digium.com><br>+ *<br>+ * See http://www.asterisk.org for more information about<br>+ * the Asterisk project. Please do not directly contact<br>+ * any of the maintainers of this project for assistance;<br>+ * the project provides a web site, mailing lists and IRC<br>+ * channels for your use.<br>+ *<br>+ * This program is free software, distributed under the terms of<br>+ * the GNU General Public License Version 2. See the LICENSE file<br>+ * at the top of the source tree.<br>+ */<br>+<br>+/*! \file<br>+ *<br>+ * \brief Data Buffer API<br>+ *<br>+ * \author Joshua Colp <jcolp@digium.com><br>+ */<br>+<br>+/*** MODULEINFO<br>+ <support_level>core</support_level><br>+ ***/<br>+<br>+#include "asterisk.h"<br>+<br>+#include "asterisk/logger.h"<br>+#include "asterisk/strings.h"<br>+#include "asterisk/data_buffer.h"<br>+#include "asterisk/linkedlists.h"<br>+<br>+/*!<br>+ * \brief Payload placed inside of the data buffer vector<br>+ */<br>+struct data_buffer_payload {<br>+ /*! \brief The payload for this position */<br>+ void *payload;<br>+ /*! \brief The provided position for this */<br>+ int pos;<br>+ /*! \brief Linked list information */<br>+ AST_LIST_ENTRY(data_buffer_payload) list;<br>+};<br>+<br>+/*!<br>+ * \brief Data buffer containing fixed number of data payloads<br>+ */<br>+struct ast_data_buffer {<br>+ /*! \brief Callback function to free a data payload */<br>+ ast_data_buffer_free_callback free_fn;<br>+ /*! \brief A linked list of data payloads */<br>+ AST_LIST_HEAD_NOLOCK(, data_buffer_payload) payloads;<br>+ /*! \brief A linked list of unused cached data payloads */<br>+ AST_LIST_HEAD_NOLOCK(, data_buffer_payload) cached_payloads;<br>+ /*! \brief The current number of data payloads in the buffer */<br>+ size_t count;<br>+ /*! \brief Maximum number of data payloads in the buffer */<br>+ size_t max;<br>+ /*! \brief The current number of data payloads in the cache */<br>+ size_t cache_count;<br>+};<br>+<br>+/*!<br>+ * \brief Helper function to allocate a data payload<br>+ */<br>+static struct data_buffer_payload *data_buffer_payload_alloc(void *payload, int pos)<br>+{<br>+ struct data_buffer_payload *data_payload;<br>+<br>+ data_payload = ast_calloc(1, sizeof(*data_payload));<br>+ if (!data_payload) {<br>+ return NULL;<br>+ }<br>+<br>+ data_payload->payload = payload;<br>+ data_payload->pos = pos;<br>+<br>+ return data_payload;<br>+}<br>+<br>+void ast_data_buffer_cache_adjust(struct ast_data_buffer *buffer)<br>+{<br>+ int difference;<br>+<br>+ ast_assert(buffer != NULL);<br>+<br>+ /* How many payloads to add or remove from the cache. We find the max number of<br>+ * payloads the cache can hold (buffer->max - buffer->count) and then subtract<br>+ * the number of payloads currently in the cache.<br>+ */<br>+ difference = (buffer->max - buffer->count) - buffer->cache_count;<br>+<br>+ if (difference == 0) {<br>+ return;<br>+ }<br>+<br>+ if (difference > 0) {<br>+ /* Add payloads to the cache */<br>+ while (difference) {<br>+ struct data_buffer_payload *buffer_payload;<br>+<br>+ buffer_payload = data_buffer_payload_alloc(NULL, -1);<br>+ if (buffer_payload) {<br>+ AST_LIST_INSERT_TAIL(&buffer->cached_payloads, buffer_payload, list);<br>+ buffer->cache_count++;<br>+ }<br>+<br>+ difference--;<br>+ }<br>+ } else if (difference < 0) {<br>+ /* Remove payloads from the cache */<br>+ while (difference) {<br>+ struct data_buffer_payload *buffer_payload;<br>+<br>+ buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list);<br>+ if (buffer_payload) {<br>+ ast_free(buffer_payload);<br>+ buffer->cache_count--;<br>+ }<br>+<br>+ difference++;<br>+ }<br>+ }<br>+<br>+}<br>+<br>+struct ast_data_buffer *ast_data_buffer_alloc(ast_data_buffer_free_callback free_fn, size_t size)<br>+{<br>+ struct ast_data_buffer *buffer;<br>+ int i;<br>+<br>+ ast_assert(free_fn != NULL);<br>+ ast_assert(size != 0);<br>+<br>+ buffer = ast_calloc(1, sizeof(*buffer));<br>+ if (!buffer) {<br>+ return NULL;<br>+ }<br>+<br>+ AST_LIST_HEAD_INIT_NOLOCK(&buffer->payloads);<br>+ AST_LIST_HEAD_INIT_NOLOCK(&buffer->cached_payloads);<br>+<br>+ buffer->free_fn = free_fn;<br>+ buffer->max = size;<br>+<br>+ for (i = 0; i < CACHED_PAYLOADS_START; ++i) {<br>+ struct data_buffer_payload *buffer_payload;<br>+<br>+ /* We don't treat this as fatal since we will try again when a put happens */<br>+ buffer_payload = data_buffer_payload_alloc(NULL, -1);<br>+ if (buffer_payload) {<br>+ AST_LIST_INSERT_TAIL(&buffer->cached_payloads, buffer_payload, list);<br>+ buffer->cache_count++;<br>+ }<br>+ }<br>+<br>+ return buffer;<br>+}<br>+<br>+void ast_data_buffer_resize(struct ast_data_buffer *buffer, size_t size)<br>+{<br>+ struct data_buffer_payload *existing_payload;<br>+ int node = 1;<br>+<br>+ ast_assert(buffer != NULL);<br>+<br>+ /* The buffer must have at least a size of 1 */<br>+ if (size < 1) {<br>+ return;<br>+ }<br>+<br>+ if (buffer->max == size) {<br>+ return;<br>+ }<br>+<br>+ /* If the size is decreasing, some payloads will need to be freed */<br>+ if (buffer->max > size) {<br>+ AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, existing_payload, list) {<br>+ if (node > size) {<br>+ AST_LIST_REMOVE_CURRENT(list);<br>+ buffer->free_fn(existing_payload->payload);<br>+ ast_free(existing_payload);<br>+ buffer->count--;<br>+ }<br>+ node++;<br>+ }<br>+ AST_LIST_TRAVERSE_SAFE_END;<br>+ }<br>+<br>+ buffer->max = size;<br>+ ast_data_buffer_cache_adjust(buffer);<br>+}<br>+<br>+int ast_data_buffer_put(struct ast_data_buffer *buffer, int pos, void *payload)<br>+{<br>+ struct data_buffer_payload *buffer_payload = NULL;<br>+ struct data_buffer_payload *existing_payload;<br>+ int inserted = 0;<br>+<br>+ ast_assert(buffer != NULL);<br>+ ast_assert(payload != NULL);<br>+<br>+ /* If the data buffer has reached its maximum size then the head goes away and<br>+ * we will reuse its buffer payload<br>+ */<br>+ if (buffer->count == buffer->max) {<br>+ buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list);<br>+ buffer->free_fn(buffer_payload->payload);<br>+ buffer->count--;<br>+<br>+ /* Update this buffer payload with its new information */<br>+ buffer_payload->payload = payload;<br>+ buffer_payload->pos = pos;<br>+ }<br>+ if (!buffer_payload && buffer->cache_count) {<br>+ buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list);<br>+ buffer->cache_count--;<br>+<br>+ /* Update the payload from the cache with its new information */<br>+ buffer_payload->payload = payload;<br>+ buffer_payload->pos = pos;<br>+ }<br>+ if (!buffer_payload) {<br>+ ast_data_buffer_cache_adjust(buffer);<br>+ buffer_payload = data_buffer_payload_alloc(payload, pos);<br>+ if (!buffer_payload) {<br>+ return -1;<br>+ }<br>+ }<br>+<br>+ /* Given the position find its ideal spot within the buffer */<br>+ AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, existing_payload, list) {<br>+ if (existing_payload->pos > pos) {<br>+ AST_LIST_INSERT_BEFORE_CURRENT(buffer_payload, list);<br>+ inserted = 1;<br>+ break;<br>+ }<br>+ }<br>+ AST_LIST_TRAVERSE_SAFE_END;<br>+<br>+ if (!inserted) {<br>+ AST_LIST_INSERT_TAIL(&buffer->payloads, buffer_payload, list);<br>+ }<br>+<br>+ buffer->count++;<br>+<br>+ return 0;<br>+}<br>+<br>+void *ast_data_buffer_get(const struct ast_data_buffer *buffer, int pos)<br>+{<br>+ struct data_buffer_payload *buffer_payload;<br>+<br>+ ast_assert(buffer != NULL);<br>+<br>+ if (pos == -1) {<br>+ buffer_payload = AST_LIST_FIRST(&buffer->payloads);<br>+<br>+ if (buffer_payload) {<br>+ return buffer_payload->payload;<br>+ }<br>+ } else {<br>+ AST_LIST_TRAVERSE(&buffer->payloads, buffer_payload, list) {<br>+ if (buffer_payload->pos == pos) {<br>+ return buffer_payload->payload;<br>+ }<br>+ }<br>+ }<br>+<br>+ return NULL;<br>+}<br>+<br>+void ast_data_buffer_free(struct ast_data_buffer *buffer)<br>+{<br>+ struct data_buffer_payload *buffer_payload;<br>+<br>+ ast_assert(buffer != NULL);<br>+<br>+ while ((buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list))) {<br>+ buffer->free_fn(buffer_payload->payload);<br>+ ast_free(buffer_payload);<br>+ }<br>+<br>+ while ((buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list))) {<br>+ ast_free(buffer_payload);<br>+ }<br>+<br>+ ast_free(buffer);<br>+}<br>+<br>+size_t ast_data_buffer_count(const struct ast_data_buffer *buffer)<br>+{<br>+ ast_assert(buffer != NULL);<br>+<br>+ return buffer->count;<br>+}<br>+<br>+size_t ast_data_buffer_max(const struct ast_data_buffer *buffer)<br>+{<br>+ ast_assert(buffer != NULL);<br>+<br>+ return buffer->max;<br>+}<br>+<br>+size_t ast_data_buffer_cache_count(const struct ast_data_buffer *buffer)<br>+{<br>+ ast_assert(buffer != NULL);<br>+<br>+ return buffer->cache_count;<br>+}<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/8606">change 8606</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/8606"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: Iff13c5d4795d52356959fe2a57360cd57dfade07 </div>
<div style="display:none"> Gerrit-Change-Number: 8606 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Benjamin Keith Ford <bford@digium.com> </div>