<p>Benjamin Keith Ford has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/8604">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/04/8604/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/8604">change 8604</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/8604"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 15 </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: 8604 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Benjamin Keith Ford <bford@digium.com> </div>