Introduce QIODevice::skip()
[ChangeLog][QtCore][QIODevice] Added skip() method to improve performance in read operations. Change-Id: I79068a3e9df108756abe37ba3d431e27e7413621 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
53357f0156
commit
ca0df4b269
@ -1878,6 +1878,128 @@ QByteArray QIODevice::peek(qint64 maxSize)
|
||||
return d->peek(maxSize);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.11
|
||||
|
||||
Skips up to \a maxSize bytes from the device. Returns the number of bytes
|
||||
actually skipped, or -1 on error.
|
||||
|
||||
This function does not wait and only discards the data that is already
|
||||
available for reading.
|
||||
|
||||
If the device is opened in text mode, end-of-line terminators are
|
||||
translated to '\n' symbols and count as a single byte identically to the
|
||||
read() and peek() behavior.
|
||||
|
||||
This function works for all devices, including sequential ones that cannot
|
||||
seek(). It is optimized to skip unwanted data after a peek() call.
|
||||
|
||||
For random-access devices, skip() can be used to seek forward from the
|
||||
current position. Negative \a maxSize values are not allowed.
|
||||
|
||||
\sa peek(), seek(), read()
|
||||
*/
|
||||
qint64 QIODevice::skip(qint64 maxSize)
|
||||
{
|
||||
Q_D(QIODevice);
|
||||
CHECK_MAXLEN(skip, qint64(-1));
|
||||
CHECK_READABLE(skip, qint64(-1));
|
||||
|
||||
const bool sequential = d->isSequential();
|
||||
|
||||
#if defined QIODEVICE_DEBUG
|
||||
printf("%p QIODevice::skip(%lld), d->pos = %lld, d->buffer.size() = %lld\n",
|
||||
this, maxSize, d->pos, d->buffer.size());
|
||||
#endif
|
||||
|
||||
if ((sequential && d->transactionStarted) || (d->openMode & QIODevice::Text) != 0)
|
||||
return d->skipByReading(maxSize);
|
||||
|
||||
// First, skip over any data in the internal buffer.
|
||||
qint64 skippedSoFar = 0;
|
||||
if (!d->buffer.isEmpty()) {
|
||||
skippedSoFar = d->buffer.skip(maxSize);
|
||||
#if defined QIODEVICE_DEBUG
|
||||
printf("%p \tskipping %lld bytes in buffer\n", this, skippedSoFar);
|
||||
#endif
|
||||
if (!sequential)
|
||||
d->pos += skippedSoFar;
|
||||
if (d->buffer.isEmpty())
|
||||
readData(nullptr, 0);
|
||||
if (skippedSoFar == maxSize)
|
||||
return skippedSoFar;
|
||||
|
||||
maxSize -= skippedSoFar;
|
||||
}
|
||||
|
||||
// Try to seek on random-access device. At this point,
|
||||
// the internal read buffer is empty.
|
||||
if (!sequential) {
|
||||
const qint64 bytesToSkip = qMin(size() - d->pos, maxSize);
|
||||
|
||||
// If the size is unknown or file position is at the end,
|
||||
// fall back to reading below.
|
||||
if (bytesToSkip > 0) {
|
||||
if (!seek(d->pos + bytesToSkip))
|
||||
return skippedSoFar ? skippedSoFar : Q_INT64_C(-1);
|
||||
if (bytesToSkip == maxSize)
|
||||
return skippedSoFar + bytesToSkip;
|
||||
|
||||
skippedSoFar += bytesToSkip;
|
||||
maxSize -= bytesToSkip;
|
||||
}
|
||||
}
|
||||
|
||||
const qint64 skipResult = d->skip(maxSize);
|
||||
if (skippedSoFar == 0)
|
||||
return skipResult;
|
||||
|
||||
if (skipResult == -1)
|
||||
return skippedSoFar;
|
||||
|
||||
return skippedSoFar + skipResult;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
qint64 QIODevicePrivate::skipByReading(qint64 maxSize)
|
||||
{
|
||||
qint64 readSoFar = 0;
|
||||
do {
|
||||
char dummy[4096];
|
||||
const qint64 readBytes = qMin<qint64>(maxSize, sizeof(dummy));
|
||||
const qint64 readResult = read(dummy, readBytes);
|
||||
|
||||
// Do not try again, if we got less data.
|
||||
if (readResult != readBytes) {
|
||||
if (readSoFar == 0)
|
||||
return readResult;
|
||||
|
||||
if (readResult == -1)
|
||||
return readSoFar;
|
||||
|
||||
return readSoFar + readResult;
|
||||
}
|
||||
|
||||
readSoFar += readResult;
|
||||
maxSize -= readResult;
|
||||
} while (maxSize > 0);
|
||||
|
||||
return readSoFar;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
qint64 QIODevicePrivate::skip(qint64 maxSize)
|
||||
{
|
||||
// Base implementation discards the data by reading into the dummy buffer.
|
||||
// It's slow, but this works for all types of devices. Subclasses can
|
||||
// reimplement this function to improve on that.
|
||||
return skipByReading(maxSize);
|
||||
}
|
||||
|
||||
/*!
|
||||
Blocks until new data is available for reading and the readyRead()
|
||||
signal has been emitted, or until \a msecs milliseconds have
|
||||
|
@ -136,6 +136,7 @@ public:
|
||||
|
||||
qint64 peek(char *data, qint64 maxlen);
|
||||
QByteArray peek(qint64 maxlen);
|
||||
qint64 skip(qint64 maxSize);
|
||||
|
||||
virtual bool waitForReadyRead(int msecs);
|
||||
virtual bool waitForBytesWritten(int msecs);
|
||||
|
@ -174,6 +174,9 @@ public:
|
||||
qint64 read(char *data, qint64 maxSize, bool peeking = false);
|
||||
virtual qint64 peek(char *data, qint64 maxSize);
|
||||
virtual QByteArray peek(qint64 maxSize);
|
||||
qint64 skipByReading(qint64 maxSize);
|
||||
// ### Qt6: consider replacing with a protected virtual QIODevice::skipData().
|
||||
virtual qint64 skip(qint64 maxSize);
|
||||
|
||||
#ifdef QT_NO_QOBJECT
|
||||
QIODevice *q_ptr;
|
||||
|
@ -55,6 +55,10 @@ private slots:
|
||||
|
||||
void readAllKeepPosition();
|
||||
void writeInTextMode();
|
||||
void skip_data();
|
||||
void skip();
|
||||
void skipAfterPeek_data();
|
||||
void skipAfterPeek();
|
||||
|
||||
void transaction_data();
|
||||
void transaction();
|
||||
@ -628,6 +632,93 @@ void tst_QIODevice::writeInTextMode()
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QIODevice::skip_data()
|
||||
{
|
||||
QTest::addColumn<bool>("sequential");
|
||||
QTest::addColumn<QByteArray>("data");
|
||||
QTest::addColumn<int>("read");
|
||||
QTest::addColumn<int>("skip");
|
||||
QTest::addColumn<int>("skipped");
|
||||
QTest::addColumn<char>("expect");
|
||||
|
||||
QByteArray bigData;
|
||||
bigData.fill('a', 20000);
|
||||
bigData[10001] = 'x';
|
||||
|
||||
bool sequential = true;
|
||||
do {
|
||||
QByteArray devName(sequential ? "sequential" : "random-access");
|
||||
|
||||
QTest::newRow(qPrintable(devName + "-small_data")) << true << QByteArray("abcdefghij")
|
||||
<< 3 << 6 << 6 << 'j';
|
||||
QTest::newRow(qPrintable(devName + "-big_data")) << true << bigData
|
||||
<< 1 << 10000 << 10000 << 'x';
|
||||
QTest::newRow(qPrintable(devName + "-beyond_the_end")) << true << bigData
|
||||
<< 1 << 20000 << 19999 << '\0';
|
||||
|
||||
sequential = !sequential;
|
||||
} while (!sequential);
|
||||
}
|
||||
|
||||
void tst_QIODevice::skip()
|
||||
{
|
||||
QFETCH(bool, sequential);
|
||||
QFETCH(QByteArray, data);
|
||||
QFETCH(int, read);
|
||||
QFETCH(int, skip);
|
||||
QFETCH(int, skipped);
|
||||
QFETCH(char, expect);
|
||||
char lastChar = 0;
|
||||
|
||||
QScopedPointer<QIODevice> dev(sequential ? (QIODevice *) new SequentialReadBuffer(&data)
|
||||
: (QIODevice *) new QBuffer(&data));
|
||||
dev->open(QIODevice::ReadOnly);
|
||||
|
||||
for (int i = 0; i < read; ++i)
|
||||
dev->getChar(nullptr);
|
||||
|
||||
QCOMPARE(dev->skip(skip), skipped);
|
||||
dev->getChar(&lastChar);
|
||||
QCOMPARE(lastChar, expect);
|
||||
}
|
||||
|
||||
void tst_QIODevice::skipAfterPeek_data()
|
||||
{
|
||||
QTest::addColumn<bool>("sequential");
|
||||
QTest::addColumn<QByteArray>("data");
|
||||
|
||||
QByteArray bigData;
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
bigData += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
QTest::newRow("sequential") << true << bigData;
|
||||
QTest::newRow("random-access") << false << bigData;
|
||||
}
|
||||
|
||||
void tst_QIODevice::skipAfterPeek()
|
||||
{
|
||||
QFETCH(bool, sequential);
|
||||
QFETCH(QByteArray, data);
|
||||
|
||||
QScopedPointer<QIODevice> dev(sequential ? (QIODevice *) new SequentialReadBuffer(&data)
|
||||
: (QIODevice *) new QBuffer(&data));
|
||||
int readSoFar = 0;
|
||||
qint64 bytesToSkip = 1;
|
||||
|
||||
dev->open(QIODevice::ReadOnly);
|
||||
forever {
|
||||
QByteArray chunk = dev->peek(bytesToSkip);
|
||||
if (chunk.isEmpty())
|
||||
break;
|
||||
|
||||
QCOMPARE(dev->skip(bytesToSkip), qint64(chunk.size()));
|
||||
QCOMPARE(chunk, data.mid(readSoFar, chunk.size()));
|
||||
readSoFar += chunk.size();
|
||||
bytesToSkip <<= 1;
|
||||
}
|
||||
QCOMPARE(readSoFar, data.size());
|
||||
}
|
||||
|
||||
void tst_QIODevice::transaction_data()
|
||||
{
|
||||
QTest::addColumn<bool>("sequential");
|
||||
|
Loading…
Reference in New Issue
Block a user