summaryrefslogtreecommitdiffstats
path: root/xbmc/utils/RingBuffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/utils/RingBuffer.cpp')
-rw-r--r--xbmc/utils/RingBuffer.cpp245
1 files changed, 245 insertions, 0 deletions
diff --git a/xbmc/utils/RingBuffer.cpp b/xbmc/utils/RingBuffer.cpp
new file mode 100644
index 0000000..454f1f9
--- /dev/null
+++ b/xbmc/utils/RingBuffer.cpp
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2010-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "RingBuffer.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <mutex>
+
+/* Constructor */
+CRingBuffer::CRingBuffer()
+{
+ m_buffer = NULL;
+ m_size = 0;
+ m_readPtr = 0;
+ m_writePtr = 0;
+ m_fillCount = 0;
+}
+
+/* Destructor */
+CRingBuffer::~CRingBuffer()
+{
+ Destroy();
+}
+
+/* Create a ring buffer with the specified 'size' */
+bool CRingBuffer::Create(unsigned int size)
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ m_buffer = (char*)malloc(size);
+ if (m_buffer != NULL)
+ {
+ m_size = size;
+ return true;
+ }
+ return false;
+}
+
+/* Free the ring buffer and set all values to NULL or 0 */
+void CRingBuffer::Destroy()
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ if (m_buffer != NULL)
+ {
+ free(m_buffer);
+ m_buffer = NULL;
+ }
+ m_size = 0;
+ m_readPtr = 0;
+ m_writePtr = 0;
+ m_fillCount = 0;
+}
+
+/* Clear the ring buffer */
+void CRingBuffer::Clear()
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ m_readPtr = 0;
+ m_writePtr = 0;
+ m_fillCount = 0;
+}
+
+/* Read in data from the ring buffer to the supplied buffer 'buf'. The amount
+ * read in is specified by 'size'.
+ */
+bool CRingBuffer::ReadData(char *buf, unsigned int size)
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ if (size > m_fillCount)
+ {
+ return false;
+ }
+ if (size + m_readPtr > m_size)
+ {
+ unsigned int chunk = m_size - m_readPtr;
+ memcpy(buf, m_buffer + m_readPtr, chunk);
+ memcpy(buf + chunk, m_buffer, size - chunk);
+ m_readPtr = size - chunk;
+ }
+ else
+ {
+ memcpy(buf, m_buffer + m_readPtr, size);
+ m_readPtr += size;
+ }
+ if (m_readPtr == m_size)
+ m_readPtr = 0;
+ m_fillCount -= size;
+ return true;
+}
+
+/* Read in data from the ring buffer to another ring buffer object specified by
+ * 'rBuf'.
+ */
+bool CRingBuffer::ReadData(CRingBuffer &rBuf, unsigned int size)
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ if (rBuf.getBuffer() == NULL)
+ rBuf.Create(size);
+
+ bool bOk = size <= rBuf.getMaxWriteSize() && size <= getMaxReadSize();
+ if (bOk)
+ {
+ unsigned int chunksize = std::min(size, m_size - m_readPtr);
+ bOk = rBuf.WriteData(&getBuffer()[m_readPtr], chunksize);
+ if (bOk && chunksize < size)
+ bOk = rBuf.WriteData(&getBuffer()[0], size - chunksize);
+ if (bOk)
+ SkipBytes(size);
+ }
+
+ return bOk;
+}
+
+/* Write data to ring buffer from buffer specified in 'buf'. Amount read in is
+ * specified by 'size'.
+ */
+bool CRingBuffer::WriteData(const char *buf, unsigned int size)
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ if (size > m_size - m_fillCount)
+ {
+ return false;
+ }
+ if (size + m_writePtr > m_size)
+ {
+ unsigned int chunk = m_size - m_writePtr;
+ memcpy(m_buffer + m_writePtr, buf, chunk);
+ memcpy(m_buffer, buf + chunk, size - chunk);
+ m_writePtr = size - chunk;
+ }
+ else
+ {
+ memcpy(m_buffer + m_writePtr, buf, size);
+ m_writePtr += size;
+ }
+ if (m_writePtr == m_size)
+ m_writePtr = 0;
+ m_fillCount += size;
+ return true;
+}
+
+/* Write data to ring buffer from another ring buffer object specified by
+ * 'rBuf'.
+ */
+bool CRingBuffer::WriteData(CRingBuffer &rBuf, unsigned int size)
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ if (m_buffer == NULL)
+ Create(size);
+
+ bool bOk = size <= rBuf.getMaxReadSize() && size <= getMaxWriteSize();
+ if (bOk)
+ {
+ unsigned int readpos = rBuf.getReadPtr();
+ unsigned int chunksize = std::min(size, rBuf.getSize() - readpos);
+ bOk = WriteData(&rBuf.getBuffer()[readpos], chunksize);
+ if (bOk && chunksize < size)
+ bOk = WriteData(&rBuf.getBuffer()[0], size - chunksize);
+ }
+
+ return bOk;
+}
+
+/* Skip bytes in buffer to be read */
+bool CRingBuffer::SkipBytes(int skipSize)
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ if (skipSize < 0)
+ {
+ return false; // skipping backwards is not supported
+ }
+
+ unsigned int size = skipSize;
+ if (size > m_fillCount)
+ {
+ return false;
+ }
+ if (size + m_readPtr > m_size)
+ {
+ unsigned int chunk = m_size - m_readPtr;
+ m_readPtr = size - chunk;
+ }
+ else
+ {
+ m_readPtr += size;
+ }
+ if (m_readPtr == m_size)
+ m_readPtr = 0;
+ m_fillCount -= size;
+ return true;
+}
+
+/* Append all content from ring buffer 'rBuf' to this ring buffer */
+bool CRingBuffer::Append(CRingBuffer &rBuf)
+{
+ return WriteData(rBuf, rBuf.getMaxReadSize());
+}
+
+/* Copy all content from ring buffer 'rBuf' to this ring buffer overwriting any existing data */
+bool CRingBuffer::Copy(CRingBuffer &rBuf)
+{
+ Clear();
+ return Append(rBuf);
+}
+
+/* Our various 'get' methods */
+char *CRingBuffer::getBuffer()
+{
+ return m_buffer;
+}
+
+unsigned int CRingBuffer::getSize()
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ return m_size;
+}
+
+unsigned int CRingBuffer::getReadPtr() const
+{
+ return m_readPtr;
+}
+
+unsigned int CRingBuffer::getWritePtr()
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ return m_writePtr;
+}
+
+unsigned int CRingBuffer::getMaxReadSize()
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ return m_fillCount;
+}
+
+unsigned int CRingBuffer::getMaxWriteSize()
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ return m_size - m_fillCount;
+}