Torc  0.1
torcsegmentedringbuffer.cpp
Go to the documentation of this file.
1 /* Class TorcSegmentedRingBuffer
2 *
3 * This file is part of the Torc project.
4 *
5 * Copyright (C) Mark Kendall 2018
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 * USA.
21 */
22 
23 // Torc
24 #include "torclogging.h"
26 
36  : m_size(Size),
37  m_data(Size, '0'),
38  m_readPosition(0),
39  m_writePosition(1), // NB avoid read == write
40  m_currentSize(0),
41  m_currentStartPosition(1),
42  m_segmentsLock(QReadWriteLock::Recursive),
43  m_segments(),
44  m_segmentRefs(),
45  m_segmentCounter(0),
46  m_maxSegments(MaxSegments),
47  m_initSegment()
48 {
49  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Allocated segmented ring buffer of size %1bytes").arg(m_size));
50 }
51 
53 {
54  QReadLocker locker1(&m_segmentsLock);
55  m_segments.clear();
56  m_segmentRefs.clear();
57 }
58 
61 {
62  int result = 0;
63  m_segmentsLock.lockForRead();
64  result = (m_readPosition - m_writePosition) - 1;
65  if (result < 0)
66  result += m_size;
67  m_segmentsLock.unlock();
68  return result;
69 }
70 
73 {
74  return m_size;
75 }
76 
79 {
80  int result = -1;
81  m_segmentsLock.lockForRead();
82  if (!m_segments.isEmpty())
83  result = m_segmentRefs.last(); //clazy:exclude=detaching-member
84  m_segmentsLock.unlock();
85  return result;
86 }
87 
93 {
94  // only the 'writer' can change the number of segments
95  int result = 0;
96  m_segmentsLock.lockForRead();
97  result = m_segments.size();
98  if (result > 0)
99  TailRef = m_segmentRefs.first(); //clazy:exclude=detaching-member
100  m_segmentsLock.unlock();
101  return result;
102 }
103 
111 {
112  QWriteLocker locker(&m_segmentsLock);
113  int result = -1;
115  {
116  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("Cannot finish segment - nothing written"));
117  return result;
118  }
119 
120  //LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Finished segmentref %1 start %2 size %3 (segments %4)")
121  // .arg(m_segmentCounter).arg(m_currentStartPosition).arg(m_currentSize).arg(m_segments.size() + 1));
122 
123  result = m_segmentCounter;
124  m_segments.enqueue(QPair<int,int>(m_currentStartPosition, m_currentSize));
127  m_currentSize = 0;
129  if (Init)
130  SaveInitSegment();
131  else
132  emit SegmentReady(result);
133  return result;
134 }
135 
137 int TorcSegmentedRingBuffer::Write(const uint8_t *Data, int Size)
138 {
139  if (!Data || Size <= 0 || Size >= m_size)
140  return -1;
141 
142  int dummyref = 0;
143  // free up space if needed
144  while ((GetBytesFree() < Size) || (GetSegmentsAvail(dummyref) > m_maxSegments))
145  {
146  if (GetSegmentsAvail(dummyref) < 1)
147  {
148  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("Segmented buffer underrun - no space for write"));
149  return -1;
150  }
151 
152  m_segmentsLock.lockForWrite();
153  QPair<int,int> segment = m_segments.dequeue();
154  int removed = m_segmentRefs.dequeue();
155  m_readPosition = (segment.first + segment.second) % m_size;
156  m_segmentsLock.unlock();
157  emit SegmentRemoved(removed);
158  }
159 
160  int copy = qMin(Size, m_size - m_writePosition);
161  memcpy((void*)(m_data.constData() + m_writePosition), Data, copy);
162  if (copy < Size) // wrap
163  memcpy((void*)m_data.constData(), Data + copy, Size - copy);
164  m_writePosition = (m_writePosition + Size) % m_size;
165  m_currentSize += Size;
166  return Size;
167 }
168 
172 int TorcSegmentedRingBuffer::Write(QByteArray *Data, int Size)
173 {
174  if (Data)
175  return Write((uint8_t*)Data->constData(), Size);
176  return 0;
177 }
178 
185 int TorcSegmentedRingBuffer::ReadSegment(uint8_t* Dst, int Max, int SegmentRef, int Offset /*=0*/)
186 {
187  if (!Dst || Max <= 0 || Max > m_size || SegmentRef < 0 || Offset < 0 || Offset > m_size)
188  return -1;
189 
190  QReadLocker locker1(&m_segmentsLock);
191 
192  if (!m_segmentRefs.contains(SegmentRef))
193  return -1;
194 
195  QPair<int,int> segment = m_segments[m_segmentRefs.indexOf(SegmentRef)];
196  if (Offset >= segment.second)
197  return -1;
198 
199  int size = qMin(Max, segment.second - Offset);
200  if (size < 1)
201  return 0;
202  int readpos = (segment.first + Offset) % m_size;
203  int read = qMin(size, m_size - readpos);
204  memcpy(Dst, m_data.constData() + readpos, read);
205  if (read < size)
206  memcpy(Dst + read, m_data.constData(), size - read);
207  return size;
208 }
209 
214 int TorcSegmentedRingBuffer::ReadSegment(QIODevice *Dst, int SegmentRef)
215 {
216  if (!Dst || SegmentRef < 0)
217  return -1;
218 
219  QReadLocker locker1(&m_segmentsLock);
220 
221  if (!m_segmentRefs.contains(SegmentRef))
222  return -1;
223 
224  QPair<int,int> segment = m_segments[m_segmentRefs.indexOf(SegmentRef)];
225  int read = qMin(segment.second, m_size - segment.first);
226  Dst->write((const char*)(m_data.constData() + segment.first), read);
227  if (read < segment.second)
228  Dst->write((const char*)m_data.constData(), segment.second - read);
229  return segment.second;
230 }
231 
234 {
235  QWriteLocker locker(&m_segmentsLock);
236  if (m_segments.size() != 1)
237  {
238  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Cannot retrieve init segment - zero or >1 segments"));
239  return;
240  }
241 
242  // sanity check
243  if (!m_initSegment.isEmpty())
244  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("Already have init segment - overwriting"));
245 
246  // copy the segment
247  QPair<int,int> segment = m_segments.dequeue();
248  m_initSegment = QByteArray(segment.second, '0');
249  int read = qMin(segment.second, m_size - segment.first);
250  memcpy((void*)m_initSegment.constData(), m_data.constData() + segment.first, read);
251  if (read < segment.second)
252  memcpy((void*)(m_initSegment.constData() + read), m_data.constData(), segment.second - read);
253 
254  // reset the ringbuffer
255  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Init segment saved (%1 bytes) - resetting ringbuffer").arg(m_initSegment.size()));
256  m_segments.clear();
257  m_segmentRefs.clear();
258  m_readPosition = 0;
259  m_writePosition = 1;
260  m_currentSize = 0;
262  m_segmentCounter = 0;
263  emit InitSegmentReady();
264 }
265 
267 QByteArray TorcSegmentedRingBuffer::GetSegment(int SegmentRef)
268 {
269  if (SegmentRef < 0)
270  return QByteArray();
271 
272  QReadLocker locker1(&m_segmentsLock);
273 
274  if (!m_segmentRefs.contains(SegmentRef))
275  return QByteArray();
276 
277  QPair<int,int> segment = m_segments[m_segmentRefs.indexOf(SegmentRef)];
278  QByteArray result(segment.second, '0');
279  int read = qMin(segment.second, m_size - segment.first);
280  memcpy((void*)result.constData(), m_data.constData() + segment.first, read);
281  if (read < segment.second)
282  memcpy(result.data() + read, m_data.constData(), segment.second - read);
283  return result;
284 }
285 
288 {
289  QReadLocker locker(&m_segmentsLock);
290  return m_initSegment;
291 }
int GetHead(void)
Return the number of the segment at the head of the queue (the newest).
QQueue< QPair< int, int > > m_segments
QByteArray GetInitSegment(void)
Return a copy of the MP4 &#39;init&#39; segment.
int GetSize(void)
Return the size of the buffer (NOT the number of segments).
int FinishSegment(bool Init)
Finish the current segment and start a new one.
int ReadSegment(uint8_t *Data, int Max, int SegmentRef, int Offset=0)
Copy Max bytes of a segment identified by SegmentRef into the memory pointed to at Dst...
void SegmentReady(int Segment)
void SegmentRemoved(int Segment)
int GetSegmentsAvail(int &TailRef)
Return the number of available segments.
int Write(QByteArray *Data, int Size)
TorcSegmentedRingBuffer(int Size, int MaxSegments)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: torclogging.h:20
void SaveInitSegment(void)
Save the MP4 &#39;init&#39; segment.
int GetBytesFree(void)
Return the number of free bytes available for writing.
QByteArray GetSegment(int SegmentRef)
Return a copy of the segment identified by SegmentRef.