diff options
Diffstat (limited to 'src/corelib/io/qiodevice.cpp')
-rw-r--r-- | src/corelib/io/qiodevice.cpp | 512 |
1 files changed, 431 insertions, 81 deletions
diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index 3c7a7d69e4..fd204b00de 100644 --- a/src/corelib/io/qiodevice.cpp +++ b/src/corelib/io/qiodevice.cpp @@ -1,31 +1,37 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** @@ -142,8 +148,16 @@ static void checkWarnMessage(const QIODevice *device, const char *function, cons \internal */ QIODevicePrivate::QIODevicePrivate() - : openMode(QIODevice::NotOpen), buffer(QIODEVICE_BUFFERSIZE), - pos(0), devicePos(0) + : openMode(QIODevice::NotOpen), + pos(0), devicePos(0), + readChannelCount(0), + writeChannelCount(0), + currentReadChannel(0), + currentWriteChannel(0), + readBufferChunkSize(QIODEVICE_BUFFERSIZE), + writeBufferChunkSize(0), + transactionPos(0), + transactionStarted(false) , baseReadLineDataCalled(false) , accessMode(Unset) #ifdef QT_NO_QOBJECT @@ -264,6 +278,21 @@ QIODevicePrivate::~QIODevicePrivate() subclassing QIODevice, remember to bypass any buffer you may use when the device is open in Unbuffered mode. + Usually, the incoming data stream from an asynchronous device is + fragmented, and chunks of data can arrive at arbitrary points in time. + To handle incomplete reads of data structures, use the transaction + mechanism implemented by QIODevice. See startTransaction() and related + functions for more details. + + Some sequential devices support communicating via multiple channels. These + channels represent separate streams of data that have the property of + independently sequenced delivery. Once the device is opened, you can + determine the number of channels by calling the readChannelCount() and + writeChannelCount() functions. To switch between channels, call + setCurrentReadChannel() and setCurrentWriteChannel(), respectively. + QIODevice also provides additional signals to handle asynchronous + communication on a per-channel basis. + \sa QBuffer, QFile, QTcpSocket */ @@ -301,8 +330,8 @@ QIODevicePrivate::~QIODevicePrivate() /*! \fn QIODevice::bytesWritten(qint64 bytes) This signal is emitted every time a payload of data has been - written to the device. The \a bytes argument is set to the number - of bytes that were written in this payload. + written to the device's current write channel. The \a bytes argument is + set to the number of bytes that were written in this payload. bytesWritten() is not emitted recursively; if you reenter the event loop or call waitForBytesWritten() inside a slot connected to the @@ -313,12 +342,28 @@ QIODevicePrivate::~QIODevicePrivate() */ /*! + \fn QIODevice::channelBytesWritten(int channel, qint64 bytes) + \since 5.7 + + This signal is emitted every time a payload of data has been written to + the device. The \a bytes argument is set to the number of bytes that were + written in this payload, while \a channel is the channel they were written + to. Unlike bytesWritten(), it is emitted regardless of the + \l{currentWriteChannel()}{current write channel}. + + channelBytesWritten() can be emitted recursively - even for the same + channel. + + \sa bytesWritten(), channelReadyRead() +*/ + +/*! \fn QIODevice::readyRead() This signal is emitted once every time new data is available for - reading from the device. It will only be emitted again once new - data is available, such as when a new payload of network data has - arrived on your network socket, or when a new block of data has + reading from the device's current read channel. It will only be emitted + again once new data is available, such as when a new payload of network + data has arrived on your network socket, or when a new block of data has been appended to your device. readyRead() is not emitted recursively; if you reenter the event loop or @@ -334,6 +379,20 @@ QIODevicePrivate::~QIODevicePrivate() \sa bytesWritten() */ +/*! + \fn QIODevice::channelReadyRead(int channel) + \since 5.7 + + This signal is emitted when new data is available for reading from the + device. The \a channel argument is set to the index of the read channel on + which the data has arrived. Unlike readyRead(), it is emitted regardless of + the \l{currentReadChannel()}{current read channel}. + + channelReadyRead() can be emitted recursively - even for the same channel. + + \sa readyRead(), channelBytesWritten() +*/ + /*! \fn QIODevice::aboutToClose() This signal is emitted when the device is about to close. Connect @@ -469,8 +528,8 @@ void QIODevice::setOpenMode(OpenMode openMode) #endif d->openMode = openMode; d->accessMode = QIODevicePrivate::Unset; - if (!isReadable()) - d->buffer.clear(); + d->setReadChannelCount(isReadable() ? qMax(d->readChannelCount, 1) : 0); + d->setWriteChannelCount(isWritable() ? qMax(d->writeChannelCount, 1) : 0); } /*! @@ -547,6 +606,139 @@ bool QIODevice::isWritable() const } /*! + \since 5.7 + + Returns the number of available read channels if the device is open; + otherwise returns 0. + + \sa writeChannelCount(), QProcess +*/ +int QIODevice::readChannelCount() const +{ + return d_func()->readChannelCount; +} + +/*! + \since 5.7 + + Returns the number of available write channels if the device is open; + otherwise returns 0. + + \sa readChannelCount() +*/ +int QIODevice::writeChannelCount() const +{ + return d_func()->writeChannelCount; +} + +/*! + \since 5.7 + + Returns the index of the current read channel. + + \sa setCurrentReadChannel(), readChannelCount(), QProcess +*/ +int QIODevice::currentReadChannel() const +{ + return d_func()->currentReadChannel; +} + +/*! + \since 5.7 + + Sets the current read channel of the QIODevice to the given \a + channel. The current input channel is used by the functions + read(), readAll(), readLine(), and getChar(). It also determines + which channel triggers QIODevice to emit readyRead(). + + \sa currentReadChannel(), readChannelCount(), QProcess +*/ +void QIODevice::setCurrentReadChannel(int channel) +{ + Q_D(QIODevice); + + if (d->transactionStarted) { + checkWarnMessage(this, "setReadChannel", "Failed due to read transaction being in progress"); + return; + } + +#if defined QIODEVICE_DEBUG + qDebug("%p QIODevice::setCurrentReadChannel(%d), d->currentReadChannel = %d, d->readChannelCount = %d\n", + this, channel, d->currentReadChannel, d->readChannelCount); +#endif + + d->setCurrentReadChannel(channel); +} + +/*! + \internal +*/ +void QIODevicePrivate::setReadChannelCount(int count) +{ + if (count > readBuffers.size()) { + readBuffers.insert(readBuffers.end(), count - readBuffers.size(), + QRingBuffer(readBufferChunkSize)); + } else { + readBuffers.resize(count); + } + readChannelCount = count; + setCurrentReadChannel(currentReadChannel); +} + +/*! + \since 5.7 + + Returns the the index of the current write channel. + + \sa setCurrentWriteChannel(), writeChannelCount() +*/ +int QIODevice::currentWriteChannel() const +{ + return d_func()->currentWriteChannel; +} + +/*! + \since 5.7 + + Sets the current write channel of the QIODevice to the given \a + channel. The current output channel is used by the functions + write(), putChar(). It also determines which channel triggers + QIODevice to emit bytesWritten(). + + \sa currentWriteChannel(), writeChannelCount() +*/ +void QIODevice::setCurrentWriteChannel(int channel) +{ + Q_D(QIODevice); + +#if defined QIODEVICE_DEBUG + qDebug("%p QIODevice::setCurrentWriteChannel(%d), d->currentWriteChannel = %d, d->writeChannelCount = %d\n", + this, channel, d->currentWriteChannel, d->writeChannelCount); +#endif + + d->setCurrentWriteChannel(channel); +} + +/*! + \internal +*/ +void QIODevicePrivate::setWriteChannelCount(int count) +{ + if (count > writeBuffers.size()) { + // If writeBufferChunkSize is zero (default value), we don't use + // QIODevice's write buffers. + if (writeBufferChunkSize != 0) { + writeBuffers.insert(writeBuffers.end(), count - writeBuffers.size(), + QRingBuffer(writeBufferChunkSize)); + } + } else { + writeBuffers.resize(count); + } + writeChannelCount = count; + setCurrentWriteChannel(currentWriteChannel); +} + +/*! Opens the device and sets its OpenMode to \a mode. Returns \c true if successful; otherwise returns \c false. This function should be called from any reimplementations of open() or other functions that open the device. @@ -558,8 +750,11 @@ bool QIODevice::open(OpenMode mode) Q_D(QIODevice); d->openMode = mode; d->pos = (mode & Append) ? size() : qint64(0); - d->buffer.clear(); d->accessMode = QIODevicePrivate::Unset; + d->readBuffers.clear(); + d->writeBuffers.clear(); + d->setReadChannelCount(isReadable() ? 1 : 0); + d->setWriteChannelCount(isWritable() ? 1 : 0); #if defined QIODEVICE_DEBUG printf("%p QIODevice::open(0x%x)\n", this, quint32(mode)); #endif @@ -588,7 +783,11 @@ void QIODevice::close() d->openMode = NotOpen; d->errorString.clear(); d->pos = 0; - d->buffer.clear(); + d->transactionStarted = false; + d->transactionPos = 0; + d->setReadChannelCount(0); + // Do not clear write buffers to allow delayed close in sockets + d->writeChannelCount = 0; } /*! @@ -660,18 +859,8 @@ bool QIODevice::seek(qint64 pos) this, pos, d->pos, d->buffer.size()); #endif - qint64 offset = pos - d->pos; - d->pos = pos; d->devicePos = pos; - - 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(offset); + d->seekBuffer(pos); #if defined QIODEVICE_DEBUG printf("%p \tafter: d->pos == %lld, d->buffer.size() == %lld\n", this, d->pos, @@ -681,6 +870,24 @@ bool QIODevice::seek(qint64 pos) } /*! + \internal +*/ +void QIODevicePrivate::seekBuffer(qint64 newPos) +{ + const qint64 offset = newPos - pos; + pos = newPos; + + if (offset < 0 || offset >= 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. + buffer.clear(); + } else { + buffer.free(offset); + } +} + +/*! Returns \c true if the current read and write position is at the end of the device (i.e. there is no more data available for reading on the device); otherwise returns \c false. @@ -695,7 +902,7 @@ bool QIODevice::seek(qint64 pos) bool QIODevice::atEnd() const { Q_D(const QIODevice); - const bool result = (d->openMode == NotOpen || (d->buffer.isEmpty() + const bool result = (d->openMode == NotOpen || (d->isBufferEmpty() && bytesAvailable() == 0)); #if defined QIODEVICE_DEBUG printf("%p QIODevice::atEnd() returns %s, d->openMode == %d, d->pos == %lld\n", this, @@ -740,18 +947,21 @@ qint64 QIODevice::bytesAvailable() const Q_D(const QIODevice); if (!d->isSequential()) return qMax(size() - d->pos, qint64(0)); - return d->buffer.size(); + return d->buffer.size() - d->transactionPos; } /*! For buffered devices, this function returns the number of bytes waiting to be written. For devices with no buffer, this function returns 0. + Subclasses that reimplement this function must call the base + implementation in order to include the size of the buffer of QIODevice. + \sa bytesAvailable(), bytesWritten(), isSequential() */ qint64 QIODevice::bytesToWrite() const { - return qint64(0); + return d_func()->writeBuffer.size(); } /*! @@ -777,9 +987,10 @@ qint64 QIODevice::read(char *data, qint64 maxSize) #endif const bool sequential = d->isSequential(); + const bool keepDataInBuffer = sequential && d->transactionStarted; // Short circuit for getChar() - if (maxSize == 1) { + if (maxSize == 1 && !keepDataInBuffer) { int chint; while ((chint = d->buffer.getChar()) != -1) { if (!sequential) @@ -806,9 +1017,13 @@ qint64 QIODevice::read(char *data, qint64 maxSize) char *readPtr = data; forever { // Try reading from the buffer. - qint64 bufferReadChunkSize = d->buffer.read(data, maxSize); + qint64 bufferReadChunkSize = keepDataInBuffer + ? d->buffer.peek(data, maxSize, d->transactionPos) + : d->buffer.read(data, maxSize); if (bufferReadChunkSize > 0) { - if (!sequential) + if (keepDataInBuffer) + d->transactionPos += bufferReadChunkSize; + else if (!sequential) d->pos += bufferReadChunkSize; readSoFar += bufferReadChunkSize; data += bufferReadChunkSize; @@ -826,7 +1041,8 @@ qint64 QIODevice::read(char *data, qint64 maxSize) // 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)) { + if ((maxSize >= d->readBufferChunkSize || (d->openMode & Unbuffered)) + && !keepDataInBuffer) { // Read big chunk directly to output buffer readFromDevice = readData(data, maxSize); deviceAtEof = (readFromDevice != maxSize); @@ -844,7 +1060,10 @@ qint64 QIODevice::read(char *data, qint64 maxSize) } } } else { - const qint64 bytesToBuffer = QIODEVICE_BUFFERSIZE; + // Do not read more than maxSize on unbuffered devices + const qint64 bytesToBuffer = (d->openMode & Unbuffered) + ? qMin(maxSize, qint64(d->readBufferChunkSize)) + : qint64(d->readBufferChunkSize); // Try to fill QIODevice buffer by single read readFromDevice = readData(d->buffer.reserve(bytesToBuffer), bytesToBuffer); deviceAtEof = (readFromDevice != bytesToBuffer); @@ -907,10 +1126,8 @@ qint64 QIODevice::read(char *data, qint64 maxSize) debugBinaryString(data - readSoFar, readSoFar); #endif - if (madeBufferReadsOnly && d->buffer.isEmpty()) { - d->buffer.clear(); + if (madeBufferReadsOnly && d->isBufferEmpty()) readData(data, 0); - } return readSoFar; } @@ -936,8 +1153,6 @@ QByteArray QIODevice::read(qint64 maxSize) #if defined QIODEVICE_DEBUG 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 >= MaxByteArraySize) { @@ -952,11 +1167,11 @@ QByteArray QIODevice::read(qint64 maxSize) // If resize fails, read incrementally. qint64 readResult; do { - result.resize(int(qMin(maxSize, result.size() + QIODEVICE_BUFFERSIZE))); + result.resize(int(qMin(maxSize, qint64(result.size() + d->readBufferChunkSize)))); readResult = read(result.data() + readBytes, result.size() - readBytes); if (readResult > 0 || readBytes == 0) readBytes += readResult; - } while (readResult == QIODEVICE_BUFFERSIZE); + } while (readResult == d->readBufferChunkSize); } else { readBytes = read(result.data(), result.size()); } @@ -992,7 +1207,9 @@ QByteArray QIODevice::readAll() qint64 readBytes = (d->isSequential() ? Q_INT64_C(0) : size()); if (readBytes == 0) { // Size is unknown, read incrementally. - qint64 readChunkSize = qMax(d->buffer.size(), QIODEVICE_BUFFERSIZE); + qint64 readChunkSize = qMax(qint64(d->readBufferChunkSize), + d->isSequential() ? (d->buffer.size() - d->transactionPos) + : d->buffer.size()); qint64 readResult; do { if (readBytes + readChunkSize >= MaxByteArraySize) { @@ -1003,7 +1220,7 @@ QByteArray QIODevice::readAll() readResult = read(result.data() + readBytes, readChunkSize); if (readResult > 0 || readBytes == 0) { readBytes += readResult; - readChunkSize = QIODEVICE_BUFFERSIZE; + readChunkSize = d->readBufferChunkSize; } } while (readResult > 0); } else { @@ -1077,21 +1294,35 @@ qint64 QIODevice::readLine(char *data, qint64 maxSize) --maxSize; const bool sequential = d->isSequential(); + const bool keepDataInBuffer = sequential && d->transactionStarted; qint64 readSoFar = 0; - if (!d->buffer.isEmpty()) { - readSoFar = d->buffer.readLine(data, maxSize); + if (keepDataInBuffer) { + if (d->transactionPos < d->buffer.size()) { + // Peek line from the specified position + const qint64 i = d->buffer.indexOf('\n', maxSize, d->transactionPos); + readSoFar = d->buffer.peek(data, i >= 0 ? (i - d->transactionPos + 1) : maxSize, + d->transactionPos); + d->transactionPos += readSoFar; + if (d->transactionPos == d->buffer.size()) + readData(data, 0); + } + } else if (!d->buffer.isEmpty()) { + // QRingBuffer::readLine() terminates the line with '\0' + readSoFar = d->buffer.readLine(data, maxSize + 1); if (d->buffer.isEmpty()) readData(data,0); if (!sequential) d->pos += readSoFar; + } + + if (readSoFar) { #if defined QIODEVICE_DEBUG printf("%p \tread from buffer: %lld bytes, last character read: %hhx\n", this, readSoFar, data[readSoFar - 1]); - if (readSoFar) - debugBinaryString(data, int(readSoFar)); + debugBinaryString(data, int(readSoFar)); #endif - if (readSoFar && data[readSoFar - 1] == '\n') { + if (data[readSoFar - 1] == '\n') { if (d->openMode & Text) { // QRingBuffer::readLine() isn't Text aware. if (readSoFar > 1 && data[readSoFar - 2] == '\r') { @@ -1107,7 +1338,11 @@ qint64 QIODevice::readLine(char *data, qint64 maxSize) if (d->pos != d->devicePos && !sequential && !seek(d->pos)) return qint64(-1); d->baseReadLineDataCalled = false; - qint64 readBytes = readLineData(data + readSoFar, maxSize - readSoFar); + // Force base implementation for transaction on sequential device + // as it stores the data in internal buffer automatically. + qint64 readBytes = keepDataInBuffer + ? QIODevice::readLineData(data + readSoFar, maxSize - readSoFar) + : readLineData(data + readSoFar, maxSize - readSoFar); #if defined QIODEVICE_DEBUG printf("%p \tread from readLineData: %lld bytes, readSoFar = %lld bytes\n", this, readBytes, readSoFar); @@ -1164,8 +1399,6 @@ QByteArray QIODevice::readLine(qint64 maxSize) #if defined QIODEVICE_DEBUG 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 >= MaxByteArraySize) { @@ -1185,11 +1418,11 @@ QByteArray QIODevice::readLine(qint64 maxSize) qint64 readResult; do { - result.resize(int(qMin(maxSize, result.size() + QIODEVICE_BUFFERSIZE))); + result.resize(int(qMin(maxSize, qint64(result.size() + d->readBufferChunkSize)))); readResult = readLine(result.data() + readBytes, result.size() - readBytes); if (readResult > 0 || readBytes == 0) readBytes += readResult; - } while (readResult == QIODEVICE_BUFFERSIZE + } while (readResult == d->readBufferChunkSize && result[int(readBytes - 1)] != '\n'); } else readBytes = readLine(result.data(), result.size()); @@ -1262,7 +1495,95 @@ qint64 QIODevice::readLineData(char *data, qint64 maxSize) */ bool QIODevice::canReadLine() const { - return d_func()->buffer.canReadLine(); + Q_D(const QIODevice); + return d->buffer.indexOf('\n', d->buffer.size(), + d->isSequential() ? d->transactionPos : Q_INT64_C(0)) >= 0; +} + +/*! + \since 5.7 + + Starts a new read transaction on the device. + + Defines a restorable point within the sequence of read operations. For + sequential devices, read data will be duplicated internally to allow + recovery in case of incomplete reads. For random-access devices, + this function saves the current position. Call commitTransaction() or + rollbackTransaction() to finish the transaction. + + \note Nesting transactions is not supported. + + \sa commitTransaction(), rollbackTransaction() +*/ +void QIODevice::startTransaction() +{ + Q_D(QIODevice); + if (d->transactionStarted) { + checkWarnMessage(this, "startTransaction", "Called while transaction already in progress"); + return; + } + d->transactionPos = d->pos; + d->transactionStarted = true; +} + +/*! + \since 5.7 + + Completes a read transaction. + + For sequential devices, all data recorded in the internal buffer during + the transaction will be discarded. + + \sa startTransaction(), rollbackTransaction() +*/ +void QIODevice::commitTransaction() +{ + Q_D(QIODevice); + if (!d->transactionStarted) { + checkWarnMessage(this, "commitTransaction", "Called while no transaction in progress"); + return; + } + if (d->isSequential()) + d->buffer.free(d->transactionPos); + d->transactionStarted = false; + d->transactionPos = 0; +} + +/*! + \since 5.7 + + Rolls back a read transaction. + + Restores the input stream to the point of the startTransaction() call. + This function is commonly used to rollback the transaction when an + incomplete read was detected prior to committing the transaction. + + \sa startTransaction(), commitTransaction() +*/ +void QIODevice::rollbackTransaction() +{ + Q_D(QIODevice); + if (!d->transactionStarted) { + checkWarnMessage(this, "rollbackTransaction", "Called while no transaction in progress"); + return; + } + if (!d->isSequential()) + d->seekBuffer(d->transactionPos); + d->transactionStarted = false; + d->transactionPos = 0; +} + +/*! + \since 5.7 + + Returns \c true if a transaction is in progress on the device, otherwise + \c false. + + \sa startTransaction() +*/ +bool QIODevice::isTransactionStarted() const +{ + return d_func()->transactionStarted; } /*! @@ -1336,13 +1657,10 @@ qint64 QIODevice::write(const char *data, qint64 maxSize) #endif qint64 written = writeData(data, maxSize); - if (written > 0) { - if (!sequential) { - d->pos += written; - d->devicePos += written; - } - if (!d->buffer.isEmpty() && !sequential) - d->buffer.skip(written); + if (!sequential && written > 0) { + d->pos += written; + d->devicePos += written; + d->buffer.skip(written); } return written; } @@ -1386,12 +1704,19 @@ qint64 QIODevice::write(const char *data) If \a c was not previously read from the device, the behavior is undefined. + + \note This function is not available while a transaction is in progress. */ void QIODevice::ungetChar(char c) { Q_D(QIODevice); CHECK_READABLE(read, Q_VOID); + if (d->transactionStarted) { + checkWarnMessage(this, "ungetChar", "Called while transaction is in progress"); + return; + } + #if defined QIODEVICE_DEBUG printf("%p QIODevice::ungetChar(0x%hhx '%c')\n", this, c, isprint(c) ? c : '?'); #endif @@ -1426,13 +1751,26 @@ bool QIODevicePrivate::putCharHelper(char c) */ qint64 QIODevicePrivate::peek(char *data, qint64 maxSize) { - qint64 readBytes = q_func()->read(data, maxSize); - if (readBytes <= 0) + Q_Q(QIODevice); + + if (transactionStarted) { + const qint64 savedTransactionPos = transactionPos; + const qint64 savedPos = pos; + + qint64 readBytes = q->read(data, maxSize); + + // Restore initial position + if (isSequential()) + transactionPos = savedTransactionPos; + else + seekBuffer(savedPos); return readBytes; + } + + q->startTransaction(); + qint64 readBytes = q->read(data, maxSize); + q->rollbackTransaction(); - buffer.ungetBlock(data, readBytes); - if (!isSequential()) - pos -= readBytes; return readBytes; } @@ -1441,14 +1779,26 @@ qint64 QIODevicePrivate::peek(char *data, qint64 maxSize) */ QByteArray QIODevicePrivate::peek(qint64 maxSize) { - QByteArray result = q_func()->read(maxSize); + Q_Q(QIODevice); + + if (transactionStarted) { + const qint64 savedTransactionPos = transactionPos; + const qint64 savedPos = pos; + + QByteArray result = q->read(maxSize); - if (result.isEmpty()) + // Restore initial position + if (isSequential()) + transactionPos = savedTransactionPos; + else + seekBuffer(savedPos); return result; + } + + q->startTransaction(); + QByteArray result = q->read(maxSize); + q->rollbackTransaction(); - buffer.ungetBlock(result.constData(), result.size()); - if (!isSequential()) - pos -= result.size(); return result; } |