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