/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtCore module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt 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 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qnoncontiguousbytedevice_p.h" | |
#include <qbuffer.h> | |
#include <qdebug.h> | |
#include <qfile.h> | |
QT_BEGIN_NAMESPACE | |
/*! | |
\class QNonContiguousByteDevice | |
\brief A QNonContiguousByteDevice is a representation of a | |
file, array or buffer that allows access with a read pointer. | |
\since 4.6 | |
\inmodule QtCore | |
The goal of this class is to have a data representation that | |
allows us to avoid doing a memcpy as we have to do with QIODevice. | |
\sa QNonContiguousByteDeviceFactory | |
\internal | |
*/ | |
/*! | |
\fn virtual const char* QNonContiguousByteDevice::readPointer(qint64 maximumLength, qint64 &len) | |
Return a byte pointer for at most \a maximumLength bytes of that device. | |
if \a maximumLength is -1, the caller does not care about the length and | |
the device may return what it desires to. | |
The actual number of bytes the pointer is valid for is returned in | |
the \a len variable. | |
\a len will be -1 if EOF or an error occurs. | |
If it was really EOF can then afterwards be checked with atEnd() | |
Returns 0 if it is not possible to read at that position. | |
\sa atEnd() | |
\internal | |
*/ | |
/*! | |
\fn virtual bool QNonContiguousByteDevice::advanceReadPointer(qint64 amount) | |
will advance the internal read pointer by \a amount bytes. | |
The old readPointer is invalid after this call. | |
\sa readPointer() | |
\internal | |
*/ | |
/*! | |
\fn virtual bool QNonContiguousByteDevice::atEnd() | |
Returns true if everything has been read and the read | |
pointer cannot be advanced anymore. | |
\sa readPointer(), advanceReadPointer(), reset() | |
\internal | |
*/ | |
/*! | |
\fn virtual bool QNonContiguousByteDevice::reset() | |
Moves the internal read pointer back to the beginning. | |
Returns false if this was not possible. | |
\sa atEnd(), disableReset() | |
\internal | |
*/ | |
/*! | |
\fn void QNonContiguousByteDevice::disableReset() | |
Disable the reset() call, e.g. it will always | |
do nothing and return false. | |
\sa reset() | |
\internal | |
*/ | |
/*! | |
\fn virtual qint64 QNonContiguousByteDevice::size() | |
Returns the size of the complete device or -1 if unknown. | |
May also return less/more than what can be actually read with readPointer() | |
\internal | |
*/ | |
/*! | |
\fn void QNonContiguousByteDevice::readyRead() | |
Emitted when there is data available | |
\internal | |
*/ | |
/*! | |
\fn void QNonContiguousByteDevice::readProgress(qint64 current, qint64 total) | |
Emitted when data has been "read" by advancing the read pointer | |
\internal | |
*/ | |
QNonContiguousByteDevice::QNonContiguousByteDevice() : QObject((QObject*)0), resetDisabled(false) | |
{ | |
} | |
QNonContiguousByteDevice::~QNonContiguousByteDevice() | |
{ | |
} | |
void QNonContiguousByteDevice::disableReset() | |
{ | |
resetDisabled = true; | |
} | |
QNonContiguousByteDeviceBufferImpl::QNonContiguousByteDeviceBufferImpl(QBuffer *b) : QNonContiguousByteDevice() | |
{ | |
buffer = b; | |
byteArray = QByteArray::fromRawData(buffer->buffer().constData() + buffer->pos(), buffer->size() - buffer->pos()); | |
arrayImpl = new QNonContiguousByteDeviceByteArrayImpl(&byteArray); | |
arrayImpl->setParent(this); | |
connect(arrayImpl, SIGNAL(readyRead()), SIGNAL(readyRead())); | |
connect(arrayImpl, SIGNAL(readProgress(qint64,qint64)), SIGNAL(readProgress(qint64,qint64))); | |
} | |
QNonContiguousByteDeviceBufferImpl::~QNonContiguousByteDeviceBufferImpl() | |
{ | |
} | |
const char* QNonContiguousByteDeviceBufferImpl::readPointer(qint64 maximumLength, qint64 &len) | |
{ | |
return arrayImpl->readPointer(maximumLength, len); | |
} | |
bool QNonContiguousByteDeviceBufferImpl::advanceReadPointer(qint64 amount) | |
{ | |
return arrayImpl->advanceReadPointer(amount); | |
} | |
bool QNonContiguousByteDeviceBufferImpl::atEnd() | |
{ | |
return arrayImpl->atEnd(); | |
} | |
bool QNonContiguousByteDeviceBufferImpl::reset() | |
{ | |
if (resetDisabled) | |
return false; | |
return arrayImpl->reset(); | |
} | |
qint64 QNonContiguousByteDeviceBufferImpl::size() | |
{ | |
return arrayImpl->size(); | |
} | |
QNonContiguousByteDeviceByteArrayImpl::QNonContiguousByteDeviceByteArrayImpl(QByteArray *ba) : QNonContiguousByteDevice(), currentPosition(0) | |
{ | |
byteArray = ba; | |
} | |
QNonContiguousByteDeviceByteArrayImpl::~QNonContiguousByteDeviceByteArrayImpl() | |
{ | |
} | |
const char* QNonContiguousByteDeviceByteArrayImpl::readPointer(qint64 maximumLength, qint64 &len) | |
{ | |
if (atEnd()) { | |
len = -1; | |
return 0; | |
} | |
if (maximumLength != -1) | |
len = qMin(maximumLength, size() - currentPosition); | |
else | |
len = size() - currentPosition; | |
return byteArray->constData() + currentPosition; | |
} | |
bool QNonContiguousByteDeviceByteArrayImpl::advanceReadPointer(qint64 amount) | |
{ | |
currentPosition += amount; | |
emit readProgress(currentPosition, size()); | |
return true; | |
} | |
bool QNonContiguousByteDeviceByteArrayImpl::atEnd() | |
{ | |
return currentPosition >= size(); | |
} | |
bool QNonContiguousByteDeviceByteArrayImpl::reset() | |
{ | |
if (resetDisabled) | |
return false; | |
currentPosition = 0; | |
return true; | |
} | |
qint64 QNonContiguousByteDeviceByteArrayImpl::size() | |
{ | |
return byteArray->size(); | |
} | |
QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QRingBuffer *rb) | |
: QNonContiguousByteDevice(), currentPosition(0) | |
{ | |
ringBuffer = rb; | |
} | |
QNonContiguousByteDeviceRingBufferImpl::~QNonContiguousByteDeviceRingBufferImpl() | |
{ | |
} | |
const char* QNonContiguousByteDeviceRingBufferImpl::readPointer(qint64 maximumLength, qint64 &len) | |
{ | |
if (atEnd()) { | |
len = -1; | |
return 0; | |
} | |
const char *returnValue = ringBuffer->readPointerAtPosition(currentPosition, len); | |
if (maximumLength != -1) | |
len = qMin(len, maximumLength); | |
return returnValue; | |
} | |
bool QNonContiguousByteDeviceRingBufferImpl::advanceReadPointer(qint64 amount) | |
{ | |
currentPosition += amount; | |
emit readProgress(currentPosition, size()); | |
return true; | |
} | |
bool QNonContiguousByteDeviceRingBufferImpl::atEnd() | |
{ | |
return currentPosition >= size(); | |
} | |
bool QNonContiguousByteDeviceRingBufferImpl::reset() | |
{ | |
if (resetDisabled) | |
return false; | |
currentPosition = 0; | |
return true; | |
} | |
qint64 QNonContiguousByteDeviceRingBufferImpl::size() | |
{ | |
return ringBuffer->size(); | |
} | |
QNonContiguousByteDeviceIoDeviceImpl::QNonContiguousByteDeviceIoDeviceImpl(QIODevice *d) | |
: QNonContiguousByteDevice(), | |
currentReadBuffer(0), currentReadBufferSize(16*1024), | |
currentReadBufferAmount(0), currentReadBufferPosition(0), totalAdvancements(0), | |
eof(false) | |
{ | |
device = d; | |
initialPosition = d->pos(); | |
connect(device, SIGNAL(readyRead()), this, SIGNAL(readyRead()), Qt::QueuedConnection); | |
connect(device, SIGNAL(readChannelFinished()), this, SIGNAL(readyRead()), Qt::QueuedConnection); | |
} | |
QNonContiguousByteDeviceIoDeviceImpl::~QNonContiguousByteDeviceIoDeviceImpl() | |
{ | |
delete currentReadBuffer; | |
} | |
const char* QNonContiguousByteDeviceIoDeviceImpl::readPointer(qint64 maximumLength, qint64 &len) | |
{ | |
if (eof == true) { | |
len = -1; | |
return 0; | |
} | |
if (currentReadBuffer == 0) | |
currentReadBuffer = new QByteArray(currentReadBufferSize, '\0'); // lazy alloc | |
if (maximumLength == -1) | |
maximumLength = currentReadBufferSize; | |
if (currentReadBufferAmount - currentReadBufferPosition > 0) { | |
len = currentReadBufferAmount - currentReadBufferPosition; | |
return currentReadBuffer->data() + currentReadBufferPosition; | |
} | |
qint64 haveRead = device->read(currentReadBuffer->data(), qMin(maximumLength, currentReadBufferSize)); | |
if ((haveRead == -1) || (haveRead == 0 && device->atEnd() && !device->isSequential())) { | |
eof = true; | |
len = -1; | |
// size was unknown before, emit a readProgress with the final size | |
if (size() == -1) | |
emit readProgress(totalAdvancements, totalAdvancements); | |
return 0; | |
} | |
currentReadBufferAmount = haveRead; | |
currentReadBufferPosition = 0; | |
len = haveRead; | |
return currentReadBuffer->data(); | |
} | |
bool QNonContiguousByteDeviceIoDeviceImpl::advanceReadPointer(qint64 amount) | |
{ | |
totalAdvancements += amount; | |
// normal advancement | |
currentReadBufferPosition += amount; | |
// advancing over that what has actually been read before | |
if (currentReadBufferPosition > currentReadBufferAmount) { | |
qint64 i = currentReadBufferPosition - currentReadBufferAmount; | |
while (i > 0) { | |
if (device->getChar(0) == false) { | |
emit readProgress(totalAdvancements - i, size()); | |
return false; // ### FIXME handle eof | |
} | |
i--; | |
} | |
currentReadBufferPosition = 0; | |
currentReadBufferAmount = 0; | |
} | |
if (size() == -1) | |
emit readProgress(totalAdvancements, totalAdvancements); | |
else | |
emit readProgress(totalAdvancements, size()); | |
return true; | |
} | |
bool QNonContiguousByteDeviceIoDeviceImpl::atEnd() | |
{ | |
return eof == true; | |
} | |
bool QNonContiguousByteDeviceIoDeviceImpl::reset() | |
{ | |
if (resetDisabled) | |
return false; | |
if (device->seek(initialPosition)) { | |
eof = false; // assume eof is false, it will be true after a read has been attempted | |
return true; | |
} | |
return false; | |
} | |
qint64 QNonContiguousByteDeviceIoDeviceImpl::size() | |
{ | |
// note that this is different from the size() implementation of QIODevice! | |
if (device->isSequential()) | |
return -1; | |
return device->size() - initialPosition; | |
} | |
QByteDeviceWrappingIoDevice::QByteDeviceWrappingIoDevice(QNonContiguousByteDevice *bd) : QIODevice((QObject*)0) | |
{ | |
byteDevice = bd; | |
connect(bd, SIGNAL(readyRead()), SIGNAL(readyRead())); | |
open(ReadOnly); | |
} | |
QByteDeviceWrappingIoDevice::~QByteDeviceWrappingIoDevice() | |
{ | |
} | |
bool QByteDeviceWrappingIoDevice::isSequential() const | |
{ | |
return (byteDevice->size() == -1); | |
} | |
bool QByteDeviceWrappingIoDevice::atEnd() const | |
{ | |
return byteDevice->atEnd(); | |
} | |
bool QByteDeviceWrappingIoDevice::reset() | |
{ | |
return byteDevice->reset(); | |
} | |
qint64 QByteDeviceWrappingIoDevice::size() const | |
{ | |
if (isSequential()) | |
return 0; | |
return byteDevice->size(); | |
} | |
qint64 QByteDeviceWrappingIoDevice::readData( char * data, qint64 maxSize) | |
{ | |
qint64 len; | |
const char *readPointer = byteDevice->readPointer(maxSize, len); | |
if (len == -1) | |
return -1; | |
memcpy(data, readPointer, len); | |
byteDevice->advanceReadPointer(len); | |
return len; | |
} | |
qint64 QByteDeviceWrappingIoDevice::writeData( const char* data, qint64 maxSize) | |
{ | |
Q_UNUSED(data); | |
Q_UNUSED(maxSize); | |
return -1; | |
} | |
/*! | |
\class QNonContiguousByteDeviceFactory | |
\since 4.6 | |
\inmodule QtCore | |
Creates a QNonContiguousByteDevice out of a QIODevice, | |
QByteArray etc. | |
\sa QNonContiguousByteDevice | |
\internal | |
*/ | |
/*! | |
\fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QIODevice *device); | |
Create a QNonContiguousByteDevice out of a QIODevice. | |
For QFile, QBuffer and all other QIoDevice, sequential or not. | |
\internal | |
*/ | |
QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QIODevice *device) | |
{ | |
// shortcut if it is a QBuffer | |
if (QBuffer* buffer = qobject_cast<QBuffer*>(device)) { | |
return new QNonContiguousByteDeviceBufferImpl(buffer); | |
} | |
// ### FIXME special case if device is a QFile that supports map() | |
// then we can actually deal with the file without using read/peek | |
// generic QIODevice | |
return new QNonContiguousByteDeviceIoDeviceImpl(device); // FIXME | |
} | |
/*! | |
\fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QRingBuffer *ringBuffer); | |
Create a QNonContiguousByteDevice out of a QRingBuffer. | |
\internal | |
*/ | |
QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QRingBuffer *ringBuffer) | |
{ | |
return new QNonContiguousByteDeviceRingBufferImpl(ringBuffer); | |
} | |
/*! | |
\fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QByteArray *byteArray); | |
Create a QNonContiguousByteDevice out of a QByteArray. | |
\internal | |
*/ | |
QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QByteArray *byteArray) | |
{ | |
return new QNonContiguousByteDeviceByteArrayImpl(byteArray); | |
} | |
/*! | |
\fn static QIODevice* QNonContiguousByteDeviceFactory::wrap(QNonContiguousByteDevice* byteDevice); | |
Wrap the \a byteDevice (possibly again) into a QIODevice. | |
\internal | |
*/ | |
QIODevice* QNonContiguousByteDeviceFactory::wrap(QNonContiguousByteDevice* byteDevice) | |
{ | |
// ### FIXME if it already has been based on QIoDevice, we could that one out again | |
// and save some calling | |
// needed for FTP backend | |
return new QByteDeviceWrappingIoDevice(byteDevice); | |
} | |
QT_END_NAMESPACE | |