5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "filteredtextbuffer.h"
24 #include <QElapsedTimer>
32 FIFOBufferDevice::FIFOBufferDevice(QObject
* parent
) :
34 m_dataBufferMaxSize(20 * 1024),
39 qint64
FIFOBufferDevice::getDataBufferMaxSize() const
41 return m_dataBufferMaxSize
;
44 void FIFOBufferDevice::setDataBufferMaxSize(qint64 size
)
46 if (m_dataBufferMaxSize
> size
)
47 trimData(m_dataBufferMaxSize
- size
);
49 m_dataBufferMaxSize
= size
;
52 // Remove data from beginning of storage array.
53 // NOT thread-safe, lock data before use (this avoids needing a recursive mutex)
54 qint64
FIFOBufferDevice::trimData(qint64 len
)
61 count
= qMin(len
, (qint64
)buffer().size());
62 buffer().remove(0, count
);
68 qint64
FIFOBufferDevice::writeData(const char * data
, qint64 len
)
73 QWriteLocker
locker(&m_dataRWLock
);
76 if (size() + len
> m_dataBufferMaxSize
) {
78 qint64 tlen
= trimData(size() + len
- m_dataBufferMaxSize
);
79 emit
bufferOverflow(tlen
);
81 else if (m_hasOverflow
) {
82 m_hasOverflow
= false;
83 emit
bufferOverflow(0);
86 // Always write to end of stream.
91 len
= QBuffer::writeData(data
, len
);
92 // Make sure we're always ready for reading, this makes bytesAvailable() (et.al.) return correct result.
98 qint64
FIFOBufferDevice::readData(char * data
, qint64 len
)
104 if (m_dataRWLock
.tryLockForRead()) {
105 // Always take data from top
107 len
= QBuffer::readData(data
, len
);
110 m_dataRWLock
.unlock();
116 qint64
FIFOBufferDevice::readLine(char * data
, qint64 maxSize
)
118 m_dataRWLock
.lockForRead();
119 qint64 len
= buffer().indexOf('\n', 0);
120 m_dataRWLock
.unlock();
122 if (len
< 0 || maxSize
<= 0)
126 len
= qMin(len
, maxSize
);
127 return readData(data
, len
);
130 QByteArray
FIFOBufferDevice::readLine(qint64 maxSize
)
133 m_dataRWLock
.lockForRead();
134 qint64 len
= buffer().indexOf('\n', 0);
135 m_dataRWLock
.unlock();
142 len
= qMin(len
, maxSize
);
145 len
= readData(ba
.data(), len
);
162 FilteredTextBuffer::FilteredTextBuffer(QObject
* parent
) :
163 FIFOBufferDevice(parent
),
164 m_inBuffer(new FIFOBufferDevice(this)),
165 m_bufferFlushTimer(new QTimer(this)),
166 m_lineFilter(QRegularExpression()),
167 m_inBuffMaxSize(5 * 1024),
168 m_inBuffFlushTimeout(1500),
169 m_lineFilterEnable(false),
170 m_lineFilterExclusive(false)
172 m_bufferFlushTimer
->setSingleShot(true);
173 setInputBufferMaxSize(m_inBuffMaxSize
);
174 setInputBufferTimeout(m_inBuffFlushTimeout
);
176 connect(m_inBuffer
, &FIFOBufferDevice::readyRead
, this, &FilteredTextBuffer::processInputBuffer
, Qt::QueuedConnection
);
177 connect(m_inBuffer
, &FIFOBufferDevice::bytesWritten
, this, &FilteredTextBuffer::onInputBufferWrite
, Qt::QueuedConnection
);
178 connect(m_inBuffer
, &FIFOBufferDevice::bufferOverflow
, this, &FilteredTextBuffer::onInputBufferOverflow
, Qt::QueuedConnection
);
179 connect(m_bufferFlushTimer
, &QTimer::timeout
, this, &FilteredTextBuffer::processInputBuffer
);
180 connect(this, &FilteredTextBuffer::timerStart
, m_bufferFlushTimer
, static_cast<void (QTimer::*)(void)>(&QTimer::start
));
181 connect(this, &FilteredTextBuffer::timerStop
, m_bufferFlushTimer
, &QTimer::stop
);
184 FilteredTextBuffer::~FilteredTextBuffer()
186 if (m_bufferFlushTimer
) {
187 disconnect(m_bufferFlushTimer
, 0, this, 0);
188 disconnect(this, 0, m_bufferFlushTimer
, 0);
189 m_bufferFlushTimer
->deleteLater();
190 m_bufferFlushTimer
= Q_NULLPTR
;
194 disconnect(m_inBuffer
, 0, this, 0);
195 m_inBuffer
->deleteLater();
196 m_inBuffer
= Q_NULLPTR
;
200 qint64
FilteredTextBuffer::getInputBufferMaxSize() const
202 return m_inBuffMaxSize
;
205 quint32
FilteredTextBuffer::getInputBufferTimeout() const
207 return m_inBuffFlushTimeout
;
210 void FilteredTextBuffer::setInputBufferMaxSize(qint64 size
)
212 m_inBuffMaxSize
= size
;
214 m_inBuffer
->setDataBufferMaxSize(size
);
217 void FilteredTextBuffer::setInputBufferTimeout(quint32 ms
)
219 m_inBuffFlushTimeout
= ms
;
220 if (m_bufferFlushTimer
)
221 m_bufferFlushTimer
->setInterval(ms
);
224 void FilteredTextBuffer::setLineFilterExpr(const QRegularExpression
& expr
)
229 setLineFilterEnabled(false);
232 void FilteredTextBuffer::setLineFilterEnabled(bool enable
)
234 if (!enable
&& m_lineFilterEnable
)
237 m_lineFilterEnable
= enable
;
240 void FilteredTextBuffer::setLineFilterExclusive(bool exclusive
)
242 m_lineFilterExclusive
= exclusive
;
245 void FilteredTextBuffer::setLineFilter(bool enable
, bool exclusive
, const QRegularExpression
& expr
)
247 setLineFilterEnabled(enable
);
248 setLineFilterExclusive(exclusive
);
249 setLineFilterExpr(expr
);
252 qint64
FilteredTextBuffer::writeDataSuper(const char * data
, qint64 len
)
257 return FIFOBufferDevice::writeData(data
, len
);
260 qint64
FilteredTextBuffer::writeData(const char * data
, qint64 len
)
265 // if filter is disabled, invalid, or input buffer failure, write directly to output buffer
266 if (!m_lineFilterEnable
|| !m_inBuffer
|| len
> m_inBuffMaxSize
|| (!m_inBuffer
->isOpen() && !openInputBuffer()))
267 return writeDataSuper(data
, len
);
269 // check for input buffer overflow
270 if (m_inBuffer
->bytesAvailable() + len
> m_inBuffMaxSize
) {
272 onInputBufferOverflow(m_inBuffer
->bytesAvailable() + len
- m_inBuffMaxSize
);
275 return m_inBuffer
->write(data
, len
);
278 void FilteredTextBuffer::flushInputBuffer()
282 if (m_inBuffer
&& m_inBuffer
->bytesAvailable()) {
283 // qDebug() << "Flushing input buffer.";
284 writeDataSuper(m_inBuffer
->readAll().constData());
288 void FilteredTextBuffer::closeInputBuffer()
290 if (!m_inBuffer
|| !m_inBuffer
->isOpen())
297 bool FilteredTextBuffer::openInputBuffer()
299 if (m_inBuffer
&& m_inBuffer
->isOpen())
302 return m_inBuffer
->open(ReadWrite
);
305 void FilteredTextBuffer::processInputBuffer()
307 if (m_lineFilterEnable
&& m_inBuffer
&& m_inBuffer
->canReadLine()) {
309 while (m_inBuffer
&& m_inBuffer
->canReadLine()) {
310 QByteArray text
= m_inBuffer
->readLine();
311 bool fltMatch
= QString(text
).contains(m_lineFilter
);
312 // check line against filter
313 if ((m_lineFilterExclusive
&& !fltMatch
) || (!m_lineFilterExclusive
&& fltMatch
)) {
314 // Write line to output buffer
315 writeDataSuper(text
.constData());
318 // restart timer if unread bytes still remain
319 if (m_inBuffer
&& m_inBuffer
->bytesAvailable() > 0)
322 else if (!m_lineFilterEnable
|| !m_inBuffer
) {
325 else if (m_bufferFlushTimer
&& !m_bufferFlushTimer
->remainingTime()) {
327 //qDebug() << "Input buffer timeout.";
330 void FilteredTextBuffer::onInputBufferWrite(qint64
)
335 void FilteredTextBuffer::onInputBufferOverflow(const qint64 len
)
337 static QElapsedTimer reportTimer
;
340 reportTimer
.invalidate();
342 else if (!reportTimer
.isValid() || reportTimer
.elapsed() > 1000 * 15) {
343 qWarning("Input data buffer overflow by %lld bytes!", len
);