1 // ***************************************************************************
2 // RollingBuffer_p.cpp (c) 2011 Derek Barnett
3 // Marth Lab, Department of Biology, Boston College
4 // ---------------------------------------------------------------------------
5 // Last modified: 10 November 2011 (DB)
6 // ---------------------------------------------------------------------------
7 // Provides a dynamic I/O FIFO byte queue, which removes bytes as they are
8 // read from the front of the buffer and grows to accept bytes being written
11 // implementation note: basically a 'smart' wrapper around 1..* ByteArrays
12 // ***************************************************************************
14 #include "api/internal/io/RollingBuffer_p.h"
15 using namespace BamTools;
16 using namespace BamTools::Internal;
24 // ------------------------------
25 // RollingBuffer implementation
26 // ------------------------------
28 RollingBuffer::RollingBuffer(size_t growth)
29 : m_bufferGrowth(growth)
31 // buffer always contains at least 1 (maybe empty) byte array
32 m_data.push_back( ByteArray() );
38 RollingBuffer::~RollingBuffer(void) { }
40 size_t RollingBuffer::BlockSize(void) const {
42 // if only one byte array in buffer <- needed?
43 if ( m_tailBufferIndex == 0 )
44 return m_tail - m_head;
46 // otherwise return remaining num bytes in first array
47 const ByteArray& first = m_data.front();
48 return first.Size() - m_head;
51 bool RollingBuffer::CanReadLine(void) const {
52 return IndexOf('\n') != string::npos;
55 void RollingBuffer::Chop(size_t n) {
58 if ( n > m_totalBufferSize )
59 m_totalBufferSize = 0;
61 m_totalBufferSize -= n;
63 // loop until target case hit
66 // if only one array, decrement tail
67 if ( m_tailBufferIndex == 0 ) {
70 // if all data chopped
71 if ( m_tail <= m_head ) {
78 // if there's room in last byte array to 'chop', just decrement tail
84 // otherwise we're going to overlap our internal byte arrays
85 // reduce our chop amount by the amount of data in the last byte array
88 // remove last byte array & set tail to it's end
91 m_tail = m_data.at(m_tailBufferIndex).Size();
94 // if buffer is now empty, reset state & clear up memory
99 void RollingBuffer::Clear(void) {
101 // remove all byte arrays (except first)
102 m_data.erase( m_data.begin()+1, m_data.end() );
104 // clear out first byte array
108 // reset index & size markers
111 m_tailBufferIndex = 0;
112 m_totalBufferSize = 0;
115 void RollingBuffer::Free(size_t n) {
117 // update buffer size
118 if ( n > m_totalBufferSize )
119 m_totalBufferSize = 0;
121 m_totalBufferSize -= n;
123 // loop until target case hit
126 const size_t blockSize = BlockSize();
128 // if there's room in current array
129 if ( n < blockSize ) {
131 // shift 'head' over @n bytes
134 // check for emptied, single byte array
135 if ( m_head == m_tail && m_tailBufferIndex == 0 ) {
143 // otherwise we need to check next byte array
144 // first update amount to remove
147 // special case - there was only 1 array
148 if ( m_data.size() == 1 ) {
149 if ( m_data.at(0).Size() != m_bufferGrowth )
150 m_data[0].Resize(m_bufferGrowth);
153 m_tailBufferIndex = 0;
157 // otherwise, remove first array and move to next iteration
163 // if buffer is now empty, reset state & clear up memory
168 size_t RollingBuffer::IndexOf(char c) const {
172 // iterate over byte arrays
173 const size_t numBuffers = m_data.size();
174 for ( size_t i = 0; i < numBuffers; ++i ) {
175 const ByteArray& current = m_data.at(i);
177 // if on first array, use head; else 0
178 const size_t start = ( (i==0) ? m_head : 0 );
180 // if on last array, set end; else use current byte array size
181 const size_t end = ( (i==m_tailBufferIndex) ? m_tail : current.Size());
183 // look through this iteration's byte array for @c
184 const char* p = current.ConstData()+start;
185 for ( size_t j = start; j < end; ++j ) {
196 bool RollingBuffer::IsEmpty(void) const {
197 return (m_tailBufferIndex == 0) && (m_tail == 0);
200 size_t RollingBuffer::Read(char* dest, size_t max) {
202 size_t bytesToRead = std::min(Size(), max);
203 size_t bytesReadSoFar = 0;
205 while ( bytesReadSoFar < bytesToRead ) {
206 const char* readPtr = ReadPointer();
207 size_t blockBytes = std::min( (bytesToRead - bytesReadSoFar), BlockSize() );
209 memcpy(dest+bytesReadSoFar, readPtr, blockBytes);
210 bytesReadSoFar += blockBytes;
214 return bytesReadSoFar;
217 size_t RollingBuffer::ReadLine(char* dest, size_t max) {
219 // if we can't read line or if max is 0
220 if ( !CanReadLine() || max == 0 )
223 // otherwise, read until we hit newline
224 size_t bytesReadSoFar = 0;
225 bool finished = false;
226 while ( !finished ) {
228 const size_t index = IndexOf('\n');
229 const char* readPtr = ReadPointer();
230 size_t bytesToRead = std::min( (index+1)-bytesReadSoFar, BlockSize() );
231 bytesToRead = std::min( bytesToRead, (max-1)-bytesReadSoFar );
232 memcpy(dest+bytesReadSoFar, readPtr, bytesToRead);
233 bytesReadSoFar += bytesToRead;
236 if ( !((bytesReadSoFar < index+1)&&(bytesReadSoFar < max-1)) )
240 // null terminate 'dest' & return numBytesRead
241 dest[bytesReadSoFar] = '\0';
242 return bytesReadSoFar;
245 const char* RollingBuffer::ReadPointer(void) const {
247 // return null if empty buffer
248 if ( m_data.empty() )
251 // otherwise return pointer to current position
252 const ByteArray& first = m_data.front();
253 return first.ConstData() + m_head;
256 char* RollingBuffer::Reserve(size_t n) {
259 if ( m_totalBufferSize == 0 ) {
260 m_data[0].Resize( std::max(m_bufferGrowth, n) );
261 m_totalBufferSize += n;
263 return m_data[m_tailBufferIndex].Data();
266 // increment buffer's byte count
267 m_totalBufferSize += n;
269 // if buffer already contains enough space to fit @n more bytes
270 if ( (m_tail + n) <= m_data.at(m_tailBufferIndex).Size() ) {
272 // fetch write pointer at current 'tail', increment tail by @n & return
273 char* ptr = m_data[m_tailBufferIndex].Data() + m_tail;
278 // if last byte array isn't half full
279 if ( m_tail < m_data.at(m_tailBufferIndex).Size()/2 ) {
281 // we'll allow simple resize
282 m_data[m_tailBufferIndex].Resize(m_tail + n);
284 // fetch write pointer at current 'tail', increment tail by @n & return
285 char* ptr = m_data[m_tailBufferIndex].Data() + m_tail;
290 // otherwise, shrink last byte array to current used size
291 m_data[m_tailBufferIndex].Resize(m_tail);
293 // then append new byte array
294 m_data.push_back( ByteArray() );
296 m_data[m_tailBufferIndex].Resize( std::max(m_bufferGrowth, n) );
299 // return write-able pointer on new array
300 return m_data[m_tailBufferIndex].Data();
303 size_t RollingBuffer::Size(void) const {
304 return m_totalBufferSize;
307 void RollingBuffer::Write(const char* src, size_t n) {
308 char* writePtr = Reserve(n);
309 memcpy(writePtr, src, n);