QIODevice: Improve read buffer use

Change buffer fill strategy to have more cached data for next read call.
This avoids unnecessarily many small reads from device implementation layer.

Change-Id: If1a039524afc03c02d2299babbfccef09f3f1cf0
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
This commit is contained in:
Alex Trotsenko 2014-07-05 16:59:19 +03:00
parent 0473d2bd34
commit 06632928af

View File

@ -783,27 +783,20 @@ qint64 QIODevice::read(char *data, qint64 maxSize)
CHECK_MAXLEN(read, qint64(-1));
qint64 readSoFar = 0;
bool moreToRead = true;
do {
bool madeBufferReadsOnly = true;
bool deviceAtEof = false;
char *readPtr = data;
forever {
// Try reading from the buffer.
int lastReadChunkSize = d->buffer.read(data, maxSize);
if (lastReadChunkSize > 0) {
*d->pPos += lastReadChunkSize;
readSoFar += lastReadChunkSize;
// fast exit when satisfied by buffer
if (lastReadChunkSize == maxSize && !(d->openMode & Text)) {
if (d->buffer.isEmpty()) {
d->buffer.clear();
readData(data, 0);
}
return readSoFar;
}
data += lastReadChunkSize;
maxSize -= lastReadChunkSize;
int bufferReadChunkSize = d->buffer.read(data, maxSize);
if (bufferReadChunkSize > 0) {
*d->pPos += bufferReadChunkSize;
readSoFar += bufferReadChunkSize;
data += bufferReadChunkSize;
maxSize -= bufferReadChunkSize;
#if defined QIODEVICE_DEBUG
printf("%p \treading %d bytes from buffer into position %d\n", this, lastReadChunkSize,
int(readSoFar) - lastReadChunkSize);
printf("%p \treading %d bytes from buffer into position %d\n", this,
bufferReadChunkSize, int(readSoFar) - bufferReadChunkSize);
#endif
} else {
if (d->firstRead) {
@ -816,78 +809,60 @@ qint64 QIODevice::read(char *data, qint64 maxSize)
d->pDevicePos = &d->seqDumpPos;
}
}
}
if (!maxSize)
return readSoFar;
if ((d->openMode & Unbuffered) == 0 && maxSize < QIODEVICE_BUFFERSIZE) {
// In buffered mode, we try to fill up the QIODevice buffer before
// we do anything else.
// buffer is empty at this point, try to fill it
const int bytesToBuffer = QIODEVICE_BUFFERSIZE;
char *writePointer = d->buffer.reserve(bytesToBuffer);
if (maxSize > 0 && !deviceAtEof) {
qint64 readFromDevice = 0;
// Make sure the device is positioned correctly.
if (d->pos != d->devicePos && !d->isSequential() && !seek(d->pos))
return readSoFar ? readSoFar : qint64(-1);
qint64 readFromDevice = readData(writePointer, bytesToBuffer);
d->buffer.chop(bytesToBuffer - (readFromDevice < 0 ? 0 : int(readFromDevice)));
if (d->pos == d->devicePos || d->isSequential() || seek(d->pos)) {
madeBufferReadsOnly = false; // fix readData attempt
if (maxSize >= QIODEVICE_BUFFERSIZE || (d->openMode & Unbuffered)) {
// Read big chunk directly to output buffer
readFromDevice = readData(data, maxSize);
deviceAtEof = (readFromDevice != maxSize);
#if defined QIODEVICE_DEBUG
printf("%p \treading %d bytes from device (total %d)\n", this,
int(readFromDevice), int(readSoFar));
#endif
if (readFromDevice > 0) {
*d->pDevicePos += readFromDevice;
#if defined QIODEVICE_DEBUG
printf("%p \treading %d from device into buffer\n", this, int(readFromDevice));
#endif
if (!d->buffer.isEmpty()) {
lastReadChunkSize = d->buffer.read(data, maxSize);
readSoFar += lastReadChunkSize;
data += lastReadChunkSize;
maxSize -= lastReadChunkSize;
*d->pPos += lastReadChunkSize;
#if defined QIODEVICE_DEBUG
printf("%p \treading %d bytes from buffer at position %d\n", this,
lastReadChunkSize, int(readSoFar));
#endif
}
}
}
}
// If we need more, try reading from the device.
if (maxSize > 0) {
// Make sure the device is positioned correctly.
if (d->pos != d->devicePos && !d->isSequential() && !seek(d->pos))
return readSoFar ? readSoFar : qint64(-1);
qint64 readFromDevice = readData(data, maxSize);
#if defined QIODEVICE_DEBUG
printf("%p \treading %d bytes from device (total %d)\n", this, int(readFromDevice), int(readSoFar));
#endif
if (readFromDevice == -1 && readSoFar == 0) {
// error and we haven't read anything: return immediately
return -1;
}
if (readFromDevice > 0) {
lastReadChunkSize += int(readFromDevice);
readSoFar += readFromDevice;
data += readFromDevice;
maxSize -= readFromDevice;
*d->pPos += readFromDevice;
*d->pDevicePos += readFromDevice;
}
} else {
const int bytesToBuffer = QIODEVICE_BUFFERSIZE;
// Try to fill QIODevice buffer by single read
readFromDevice = readData(d->buffer.reserve(bytesToBuffer), bytesToBuffer);
deviceAtEof = (readFromDevice != bytesToBuffer);
d->buffer.chop(bytesToBuffer - qMax(0, int(readFromDevice)));
if (readFromDevice > 0) {
*d->pDevicePos += readFromDevice;
#if defined QIODEVICE_DEBUG
printf("%p \treading %d from device into buffer\n", this,
int(readFromDevice));
#endif
continue;
}
}
} else {
readFromDevice = -1;
}
// Best attempt has been made to read data, don't try again except for text mode adjustment below
moreToRead = false;
if (readSoFar && d->openMode & Text) {
char *readPtr = data - lastReadChunkSize;
if (readFromDevice < 0 && readSoFar == 0) {
// error and we haven't read anything: return immediately
return qint64(-1);
}
}
if ((d->openMode & Text) && readPtr < data) {
const char *endPtr = data;
if (readPtr < endPtr) {
// optimization to avoid initial self-assignment
while (*readPtr != '\r') {
if (++readPtr == endPtr)
return readSoFar;
break;
}
char *writePtr = readPtr;
@ -906,10 +881,12 @@ qint64 QIODevice::read(char *data, qint64 maxSize)
// Make sure we get more data if there is room for more. This
// is very important for when someone seeks to the start of a
// '\r\n' and reads one character - they should get the '\n'.
moreToRead = (readPtr != writePtr);
readPtr = data;
continue;
}
break;
}
} while (moreToRead);
#if defined QIODEVICE_DEBUG
printf("%p \treturning %d, d->pos == %d, d->buffer.size() == %d\n", this,
@ -917,8 +894,10 @@ qint64 QIODevice::read(char *data, qint64 maxSize)
debugBinaryString(data - readSoFar, readSoFar);
#endif
if (d->buffer.isEmpty())
if (madeBufferReadsOnly && d->buffer.isEmpty()) {
d->buffer.clear();
readData(data, 0);
}
return readSoFar;
}