src/corelib/io/qnoncontiguousbytedevice.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/corelib/io/qnoncontiguousbytedevice.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,543 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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
+