QT_BEGIN_NAMESPACE
+#define QBYTEARRAY_MAX INT_MAX
+
#ifdef QIODEVICE_DEBUG
void debugBinaryString(const QByteArray &input)
{
#define CHECK_WRITABLE(function, returnType) \
do { \
if ((d->openMode & WriteOnly) == 0) { \
- if (d->openMode == NotOpen) \
+ if (d->openMode == NotOpen) { \
+ qWarning("QIODevice::"#function": device not open"); \
return returnType; \
+ } \
qWarning("QIODevice::"#function": ReadOnly device"); \
return returnType; \
} \
#define CHECK_READABLE(function, returnType) \
do { \
if ((d->openMode & ReadOnly) == 0) { \
- if (d->openMode == NotOpen) \
+ if (d->openMode == NotOpen) { \
+ qWarning("QIODevice::"#function": device not open"); \
return returnType; \
+ } \
qWarning("QIODevice::"#function": WriteOnly device"); \
return returnType; \
} \
*/
QIODevicePrivate::QIODevicePrivate()
: openMode(QIODevice::NotOpen), buffer(QIODEVICE_BUFFERSIZE),
- pos(0), devicePos(0), seqDumpPos(0)
- , pPos(&pos), pDevicePos(&devicePos)
+ pos(0), devicePos(0)
, baseReadLineDataCalled(false)
, firstRead(true)
, accessMode(Unset)
\value NotOpen The device is not open.
\value ReadOnly The device is open for reading.
- \value WriteOnly The device is open for writing.
+ \value WriteOnly The device is open for writing. Note that this mode implies
+ Truncate.
\value ReadWrite The device is open for reading and writing.
- \value Append The device is opened in append mode, so that all data is
+ \value Append The device is opened in append mode so that all data is
written to the end of the file.
\value Truncate If possible, the device is truncated before it is opened.
All earlier contents of the device are lost.
d->openMode = NotOpen;
d->errorString.clear();
d->pos = 0;
- d->seqDumpPos = 0;
d->buffer.clear();
d->firstRead = true;
}
{
Q_D(const QIODevice);
#if defined QIODEVICE_DEBUG
- printf("%p QIODevice::pos() == %d\n", this, int(d->pos));
+ printf("%p QIODevice::pos() == %lld\n", this, d->pos);
#endif
return d->pos;
}
/*!
For random-access devices, this function sets the current position
to \a pos, returning true on success, or false if an error occurred.
- For sequential devices, the default behavior is to do nothing and
- return false.
+ For sequential devices, the default behavior is to produce a warning
+ and return false.
When subclassing QIODevice, you must call QIODevice::seek() at the
start of your function to ensure integrity with QIODevice's
- built-in buffer. The base implementation always returns true.
+ built-in buffer.
\sa pos(), isSequential()
*/
bool QIODevice::seek(qint64 pos)
{
Q_D(QIODevice);
+ if (d->isSequential()) {
+ qWarning("QIODevice::seek: Cannot call seek on a sequential device");
+ return false;
+ }
if (d->openMode == NotOpen) {
qWarning("QIODevice::seek: The device is not open");
return false;
}
if (pos < 0) {
- qWarning("QIODevice::seek: Invalid pos: %d", int(pos));
+ qWarning("QIODevice::seek: Invalid pos: %lld", pos);
return false;
}
#if defined QIODEVICE_DEBUG
- printf("%p QIODevice::seek(%d), before: d->pos = %d, d->buffer.size() = %d\n",
- this, int(pos), int(d->pos), d->buffer.size());
+ printf("%p QIODevice::seek(%lld), before: d->pos = %lld, d->buffer.size() = %lld\n",
+ this, pos, d->pos, d->buffer.size());
#endif
qint64 offset = pos - d->pos;
d->devicePos = pos;
}
- if (offset < 0
- || offset >= qint64(d->buffer.size()))
+ if (offset < 0 || offset >= d->buffer.size())
// When seeking backwards, an operation that is only allowed for
// random-access devices, the buffer is cleared. The next read
// operation will then refill the buffer. We can optimize this, if we
// find that seeking backwards becomes a significant performance hit.
d->buffer.clear();
else if (!d->buffer.isEmpty())
- d->buffer.skip(int(offset));
+ d->buffer.skip(offset);
#if defined QIODEVICE_DEBUG
- printf("%p \tafter: d->pos == %d, d->buffer.size() == %d\n", this, int(d->pos),
+ printf("%p \tafter: d->pos == %lld, d->buffer.size() == %lld\n", this, d->pos,
d->buffer.size());
#endif
return true;
{
Q_D(const QIODevice);
#if defined QIODEVICE_DEBUG
- printf("%p QIODevice::atEnd() returns %s, d->openMode == %d, d->pos == %d\n", this, (d->openMode == NotOpen || d->pos == size()) ? "true" : "false",
- int(d->openMode), int(d->pos));
+ printf("%p QIODevice::atEnd() returns %s, d->openMode == %d, d->pos == %lld\n", this,
+ (d->openMode == NotOpen || d->pos == size()) ? "true" : "false", int(d->openMode),
+ d->pos);
#endif
return d->openMode == NotOpen || (d->buffer.isEmpty() && bytesAvailable() == 0);
}
Q_D(QIODevice);
#if defined QIODEVICE_DEBUG
- printf("%p QIODevice::read(%p, %d), d->pos = %d, d->buffer.size() = %d\n",
- this, data, int(maxSize), int(d->pos), int(d->buffer.size()));
+ printf("%p QIODevice::read(%p, %lld), d->pos = %lld, d->buffer.size() = %lld\n",
+ this, data, maxSize, d->pos, d->buffer.size());
#endif
+ const bool sequential = d->isSequential();
+
// Short circuit for getChar()
if (maxSize == 1) {
int chint;
while ((chint = d->buffer.getChar()) != -1) {
- ++(*d->pPos);
+ if (!sequential)
+ ++d->pos;
char c = char(uchar(chint));
if (c == '\r' && (d->openMode & Text))
printf("%p \tread 0x%hhx (%c) returning 1 (shortcut)\n", this,
int(c), isprint(c) ? c : '?');
#endif
+ if (d->buffer.isEmpty())
+ readData(data, 0);
return qint64(1);
}
}
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))
- return readSoFar;
-
- data += lastReadChunkSize;
- maxSize -= lastReadChunkSize;
+ qint64 bufferReadChunkSize = d->buffer.read(data, maxSize);
+ if (bufferReadChunkSize > 0) {
+ if (!sequential)
+ d->pos += 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 %lld bytes from buffer into position %lld\n", this,
+ bufferReadChunkSize, readSoFar - bufferReadChunkSize);
#endif
} else {
if (d->firstRead) {
// for fast pos updates.
CHECK_READABLE(read, qint64(-1));
d->firstRead = false;
- if (d->isSequential()) {
- d->pPos = &d->seqDumpPos;
- 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
- int bytesToBuffer = QIODEVICE_BUFFERSIZE;
- char *writePointer = d->buffer.reserve(bytesToBuffer);
-
- // 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 (readFromDevice > 0) {
- *d->pDevicePos += readFromDevice;
+ if (maxSize > 0 && !deviceAtEof) {
+ qint64 readFromDevice = 0;
+ // Make sure the device is positioned correctly.
+ if (sequential || d->pos == d->devicePos || 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 from device into buffer\n", this, int(readFromDevice));
+ printf("%p \treading %lld bytes from device (total %lld)\n", this,
+ readFromDevice, readSoFar);
#endif
-
- if (!d->buffer.isEmpty()) {
- lastReadChunkSize = d->buffer.read(data, maxSize);
- readSoFar += lastReadChunkSize;
- data += lastReadChunkSize;
- maxSize -= lastReadChunkSize;
- *d->pPos += lastReadChunkSize;
+ if (readFromDevice > 0) {
+ readSoFar += readFromDevice;
+ data += readFromDevice;
+ maxSize -= readFromDevice;
+ if (!sequential) {
+ d->pos += readFromDevice;
+ d->devicePos += readFromDevice;
+ }
+ }
+ } else {
+ const qint64 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(Q_INT64_C(0), readFromDevice));
+ if (readFromDevice > 0) {
+ if (!sequential)
+ d->devicePos += readFromDevice;
#if defined QIODEVICE_DEBUG
- printf("%p \treading %d bytes from buffer at position %d\n", this,
- lastReadChunkSize, int(readSoFar));
+ printf("%p \treading %lld from device into buffer\n", this,
+ readFromDevice);
#endif
+ continue;
}
}
+ } else {
+ readFromDevice = -1;
}
- }
- // 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) {
+ if (readFromDevice < 0 && 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;
+ return qint64(-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 ((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;
- }
+ // optimization to avoid initial self-assignment
+ while (*readPtr != '\r') {
+ if (++readPtr == endPtr)
+ break;
+ }
- char *writePtr = readPtr;
+ char *writePtr = readPtr;
- while (readPtr < endPtr) {
- char ch = *readPtr++;
- if (ch != '\r')
- *writePtr++ = ch;
- else {
- --readSoFar;
- --data;
- ++maxSize;
- }
+ while (readPtr < endPtr) {
+ char ch = *readPtr++;
+ if (ch != '\r')
+ *writePtr++ = ch;
+ else {
+ --readSoFar;
+ --data;
+ ++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);
}
+
+ // 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'.
+ readPtr = data;
+ continue;
}
- } while (moreToRead);
+
+ break;
+ }
#if defined QIODEVICE_DEBUG
- printf("%p \treturning %d, d->pos == %d, d->buffer.size() == %d\n", this,
- int(readSoFar), int(d->pos), d->buffer.size());
+ printf("%p \treturning %lld, d->pos == %lld, d->buffer.size() == %lld\n", this,
+ readSoFar, d->pos, d->buffer.size());
debugBinaryString(data - readSoFar, readSoFar);
#endif
+ if (madeBufferReadsOnly && d->buffer.isEmpty()) {
+ d->buffer.clear();
+ readData(data, 0);
+ }
+
return readSoFar;
}
CHECK_MAXLEN(read, result);
#if defined QIODEVICE_DEBUG
- printf("%p QIODevice::read(%d), d->pos = %d, d->buffer.size() = %d\n",
- this, int(maxSize), int(d->pos), int(d->buffer.size()));
+ printf("%p QIODevice::read(%lld), d->pos = %lld, d->buffer.size() = %lld\n",
+ this, maxSize, d->pos, d->buffer.size());
#else
Q_UNUSED(d);
#endif
- if (maxSize != qint64(int(maxSize))) {
+ if (quint64(maxSize) >= QBYTEARRAY_MAX) {
qWarning("QIODevice::read: maxSize argument exceeds QByteArray size limit");
- maxSize = INT_MAX;
+ maxSize = QBYTEARRAY_MAX - 1;
}
qint64 readBytes = 0;
{
Q_D(QIODevice);
#if defined QIODEVICE_DEBUG
- printf("%p QIODevice::readAll(), d->pos = %d, d->buffer.size() = %d\n",
- this, int(d->pos), int(d->buffer.size()));
+ printf("%p QIODevice::readAll(), d->pos = %lld, d->buffer.size() = %lld\n",
+ this, d->pos, d->buffer.size());
#endif
QByteArray result;
qint64 readBytes = 0;
+ const bool sequential = d->isSequential();
// flush internal read buffer
if (!(d->openMode & Text) && !d->buffer.isEmpty()) {
+ if (quint64(d->buffer.size()) >= QBYTEARRAY_MAX)
+ return QByteArray();
result = d->buffer.readAll();
readBytes = result.size();
- d->pos += readBytes;
+ if (!sequential)
+ d->pos += readBytes;
}
qint64 theSize;
- if (d->isSequential() || (theSize = size()) == 0) {
+ if (sequential || (theSize = size()) == 0) {
// Size is unknown, read incrementally.
qint64 readResult;
do {
- result.resize(result.size() + QIODEVICE_BUFFERSIZE);
- readResult = read(result.data() + readBytes, result.size() - readBytes);
+ if (quint64(readBytes) + QIODEVICE_BUFFERSIZE >= QBYTEARRAY_MAX) {
+ // If resize would fail, don't read more, return what we have.
+ break;
+ }
+ result.resize(readBytes + QIODEVICE_BUFFERSIZE);
+ readResult = read(result.data() + readBytes, QIODEVICE_BUFFERSIZE);
if (readResult > 0 || readBytes == 0)
readBytes += readResult;
} while (readResult > 0);
} else {
// Read it all in one go.
// If resize fails, don't read anything.
+ if (quint64(readBytes + theSize - d->pos) >= QBYTEARRAY_MAX)
+ return QByteArray();
result.resize(int(readBytes + theSize - d->pos));
readBytes += read(result.data() + readBytes, result.size() - readBytes);
}
}
#if defined QIODEVICE_DEBUG
- printf("%p QIODevice::readLine(%p, %d), d->pos = %d, d->buffer.size() = %d\n",
- this, data, int(maxSize), int(d->pos), int(d->buffer.size()));
+ printf("%p QIODevice::readLine(%p, %lld), d->pos = %lld, d->buffer.size() = %lld\n",
+ this, data, maxSize, d->pos, d->buffer.size());
#endif
// Leave room for a '\0'
qint64 readSoFar = 0;
if (!d->buffer.isEmpty()) {
readSoFar = d->buffer.readLine(data, maxSize);
+ if (d->buffer.isEmpty())
+ readData(data, 0);
if (!sequential)
d->pos += readSoFar;
#if defined QIODEVICE_DEBUG
- printf("%p \tread from buffer: %d bytes, last character read: %hhx\n", this,
- int(readSoFar), data[int(readSoFar) - 1]);
+ printf("%p \tread from buffer: %lld bytes, last character read: %hhx\n", this,
+ readSoFar, data[readSoFar - 1]);
if (readSoFar)
debugBinaryString(data, int(readSoFar));
#endif
d->baseReadLineDataCalled = false;
qint64 readBytes = readLineData(data + readSoFar, maxSize - readSoFar);
#if defined QIODEVICE_DEBUG
- printf("%p \tread from readLineData: %d bytes, readSoFar = %d bytes\n", this,
- int(readBytes), int(readSoFar));
+ printf("%p \tread from readLineData: %lld bytes, readSoFar = %lld bytes\n", this,
+ readBytes, readSoFar);
if (readBytes > 0) {
debugBinaryString(data, int(readSoFar + readBytes));
}
}
#if defined QIODEVICE_DEBUG
- printf("%p \treturning %d, d->pos = %d, d->buffer.size() = %d, size() = %d\n",
- this, int(readSoFar), int(d->pos), d->buffer.size(), int(size()));
+ printf("%p \treturning %lld, d->pos = %lld, d->buffer.size() = %lld, size() = %lld\n",
+ this, readSoFar, d->pos, d->buffer.size(), size());
debugBinaryString(data, int(readSoFar));
#endif
return readSoFar;
CHECK_MAXLEN(readLine, result);
#if defined QIODEVICE_DEBUG
- printf("%p QIODevice::readLine(%d), d->pos = %d, d->buffer.size() = %d\n",
- this, int(maxSize), int(d->pos), int(d->buffer.size()));
+ printf("%p QIODevice::readLine(%lld), d->pos = %lld, d->buffer.size() = %lld\n",
+ this, maxSize, d->pos, d->buffer.size());
#else
Q_UNUSED(d);
#endif
- if (maxSize > INT_MAX) {
+ if (quint64(maxSize) >= QBYTEARRAY_MAX) {
qWarning("QIODevice::read: maxSize argument exceeds QByteArray size limit");
- maxSize = INT_MAX;
+ maxSize = QBYTEARRAY_MAX - 1;
}
result.resize(int(maxSize));
if (!result.size()) {
// If resize fails or maxSize == 0, read incrementally
if (maxSize == 0)
- maxSize = INT_MAX;
+ maxSize = QBYTEARRAY_MAX - 1;
// The first iteration needs to leave an extra byte for the terminating null
result.resize(1);
}
#if defined QIODEVICE_DEBUG
- printf("%p QIODevice::readLineData(%p, %d), d->pos = %d, d->buffer.size() = %d, returns %d\n",
- this, data, int(maxSize), int(d->pos), int(d->buffer.size()), int(readSoFar));
+ printf("%p QIODevice::readLineData(%p, %lld), d->pos = %lld, d->buffer.size() = %lld, "
+ "returns %lld\n", this, data, maxSize, d->pos, d->buffer.size(), readSoFar);
#endif
if (lastReadReturn != 1 && readSoFar == 0)
return isSequential() ? lastReadReturn : -1;
return readBytes;
buffer.ungetBlock(data, readBytes);
- *pPos -= readBytes;
+ if (!isSequential())
+ pos -= readBytes;
return readBytes;
}
return result;
buffer.ungetBlock(result.constData(), result.size());
- *pPos -= result.size();
+ if (!isSequential())
+ pos -= result.size();
return result;
}
all the requested information was read and therefore does not retry reading
if there was a problem.
+ This function might be called with a maxSize of 0, which can be used to
+ perform post-reading operations.
+
\sa read() readLine() writeData()
*/