1 /*
2  *  Copyright (C) 2010-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "RingBuffer.h"
10 
11 #include "threads/SingleLock.h"
12 
13 #include <algorithm>
14 #include <cstdlib>
15 #include <cstring>
16 
17 /* Constructor */
CRingBuffer()18 CRingBuffer::CRingBuffer()
19 {
20   m_buffer = NULL;
21   m_size = 0;
22   m_readPtr = 0;
23   m_writePtr = 0;
24   m_fillCount = 0;
25 }
26 
27 /* Destructor */
~CRingBuffer()28 CRingBuffer::~CRingBuffer()
29 {
30   Destroy();
31 }
32 
33 /* Create a ring buffer with the specified 'size' */
Create(unsigned int size)34 bool CRingBuffer::Create(unsigned int size)
35 {
36   CSingleLock lock(m_critSection);
37   m_buffer = (char*)malloc(size);
38   if (m_buffer != NULL)
39   {
40     m_size = size;
41     return true;
42   }
43   return false;
44 }
45 
46 /* Free the ring buffer and set all values to NULL or 0 */
Destroy()47 void CRingBuffer::Destroy()
48 {
49   CSingleLock lock(m_critSection);
50   if (m_buffer != NULL)
51   {
52     free(m_buffer);
53     m_buffer = NULL;
54   }
55   m_size = 0;
56   m_readPtr = 0;
57   m_writePtr = 0;
58   m_fillCount = 0;
59 }
60 
61 /* Clear the ring buffer */
Clear()62 void CRingBuffer::Clear()
63 {
64   CSingleLock lock(m_critSection);
65   m_readPtr = 0;
66   m_writePtr = 0;
67   m_fillCount = 0;
68 }
69 
70 /* Read in data from the ring buffer to the supplied buffer 'buf'. The amount
71  * read in is specified by 'size'.
72  */
ReadData(char * buf,unsigned int size)73 bool CRingBuffer::ReadData(char *buf, unsigned int size)
74 {
75   CSingleLock lock(m_critSection);
76   if (size > m_fillCount)
77   {
78     return false;
79   }
80   if (size + m_readPtr > m_size)
81   {
82     unsigned int chunk = m_size - m_readPtr;
83     memcpy(buf, m_buffer + m_readPtr, chunk);
84     memcpy(buf + chunk, m_buffer, size - chunk);
85     m_readPtr = size - chunk;
86   }
87   else
88   {
89     memcpy(buf, m_buffer + m_readPtr, size);
90     m_readPtr += size;
91   }
92   if (m_readPtr == m_size)
93     m_readPtr = 0;
94   m_fillCount -= size;
95   return true;
96 }
97 
98 /* Read in data from the ring buffer to another ring buffer object specified by
99  * 'rBuf'.
100  */
ReadData(CRingBuffer & rBuf,unsigned int size)101 bool CRingBuffer::ReadData(CRingBuffer &rBuf, unsigned int size)
102 {
103   CSingleLock lock(m_critSection);
104   if (rBuf.getBuffer() == NULL)
105     rBuf.Create(size);
106 
107   bool bOk = size <= rBuf.getMaxWriteSize() && size <= getMaxReadSize();
108   if (bOk)
109   {
110     unsigned int chunksize = std::min(size, m_size - m_readPtr);
111     bOk = rBuf.WriteData(&getBuffer()[m_readPtr], chunksize);
112     if (bOk && chunksize < size)
113       bOk = rBuf.WriteData(&getBuffer()[0], size - chunksize);
114     if (bOk)
115       SkipBytes(size);
116   }
117 
118   return bOk;
119 }
120 
121 /* Write data to ring buffer from buffer specified in 'buf'. Amount read in is
122  * specified by 'size'.
123  */
WriteData(const char * buf,unsigned int size)124 bool CRingBuffer::WriteData(const char *buf, unsigned int size)
125 {
126   CSingleLock lock(m_critSection);
127   if (size > m_size - m_fillCount)
128   {
129     return false;
130   }
131   if (size + m_writePtr > m_size)
132   {
133     unsigned int chunk = m_size - m_writePtr;
134     memcpy(m_buffer + m_writePtr, buf, chunk);
135     memcpy(m_buffer, buf + chunk, size - chunk);
136     m_writePtr = size - chunk;
137   }
138   else
139   {
140     memcpy(m_buffer + m_writePtr, buf, size);
141     m_writePtr += size;
142   }
143   if (m_writePtr == m_size)
144     m_writePtr = 0;
145   m_fillCount += size;
146   return true;
147 }
148 
149 /* Write data to ring buffer from another ring buffer object specified by
150  * 'rBuf'.
151  */
WriteData(CRingBuffer & rBuf,unsigned int size)152 bool CRingBuffer::WriteData(CRingBuffer &rBuf, unsigned int size)
153 {
154   CSingleLock lock(m_critSection);
155   if (m_buffer == NULL)
156     Create(size);
157 
158   bool bOk = size <= rBuf.getMaxReadSize() && size <= getMaxWriteSize();
159   if (bOk)
160   {
161     unsigned int readpos = rBuf.getReadPtr();
162     unsigned int chunksize = std::min(size, rBuf.getSize() - readpos);
163     bOk = WriteData(&rBuf.getBuffer()[readpos], chunksize);
164     if (bOk && chunksize < size)
165       bOk = WriteData(&rBuf.getBuffer()[0], size - chunksize);
166   }
167 
168   return bOk;
169 }
170 
171 /* Skip bytes in buffer to be read */
SkipBytes(int skipSize)172 bool CRingBuffer::SkipBytes(int skipSize)
173 {
174   CSingleLock lock(m_critSection);
175   if (skipSize < 0)
176   {
177     return false; // skipping backwards is not supported
178   }
179 
180   unsigned int size = skipSize;
181   if (size > m_fillCount)
182   {
183     return false;
184   }
185   if (size + m_readPtr > m_size)
186   {
187     unsigned int chunk = m_size - m_readPtr;
188     m_readPtr = size - chunk;
189   }
190   else
191   {
192     m_readPtr += size;
193   }
194   if (m_readPtr == m_size)
195     m_readPtr = 0;
196   m_fillCount -= size;
197   return true;
198 }
199 
200 /* Append all content from ring buffer 'rBuf' to this ring buffer */
Append(CRingBuffer & rBuf)201 bool CRingBuffer::Append(CRingBuffer &rBuf)
202 {
203   return WriteData(rBuf, rBuf.getMaxReadSize());
204 }
205 
206 /* Copy all content from ring buffer 'rBuf' to this ring buffer overwriting any existing data */
Copy(CRingBuffer & rBuf)207 bool CRingBuffer::Copy(CRingBuffer &rBuf)
208 {
209   Clear();
210   return Append(rBuf);
211 }
212 
213 /* Our various 'get' methods */
getBuffer()214 char *CRingBuffer::getBuffer()
215 {
216   return m_buffer;
217 }
218 
getSize()219 unsigned int CRingBuffer::getSize()
220 {
221   CSingleLock lock(m_critSection);
222   return m_size;
223 }
224 
getReadPtr() const225 unsigned int CRingBuffer::getReadPtr() const
226 {
227   return m_readPtr;
228 }
229 
getWritePtr()230 unsigned int CRingBuffer::getWritePtr()
231 {
232   CSingleLock lock(m_critSection);
233   return m_writePtr;
234 }
235 
getMaxReadSize()236 unsigned int CRingBuffer::getMaxReadSize()
237 {
238   CSingleLock lock(m_critSection);
239   return m_fillCount;
240 }
241 
getMaxWriteSize()242 unsigned int CRingBuffer::getMaxWriteSize()
243 {
244   CSingleLock lock(m_critSection);
245   return m_size - m_fillCount;
246 }
247