diff options
29 files changed, 624 insertions, 1429 deletions
diff --git a/src/plugins/qmltooling/packetprotocol/packetprotocol.pro b/src/plugins/qmltooling/packetprotocol/packetprotocol.pro new file mode 100644 index 0000000000..a6b8f26bb1 --- /dev/null +++ b/src/plugins/qmltooling/packetprotocol/packetprotocol.pro @@ -0,0 +1,11 @@ +TARGET = QtPacketProtocol +QT = core-private qml-private +CONFIG += static internal_module + +HEADERS = \ + qpacketprotocol_p.h + +SOURCES = \ + qpacketprotocol.cpp + +load(qt_module) diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp new file mode 100644 index 0000000000..9f9dbfee90 --- /dev/null +++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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. +** +** 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. +** +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpacketprotocol_p.h" + +#include <QtCore/QElapsedTimer> +#include <private/qiodevice_p.h> +#include <private/qobject_p.h> +#include <private/qpacket_p.h> + +QT_BEGIN_NAMESPACE + +static const int MAX_PACKET_SIZE = 0x7FFFFFFF; + +/*! + \class QPacketProtocol + \internal + + \brief The QPacketProtocol class encapsulates communicating discrete packets + across fragmented IO channels, such as TCP sockets. + + QPacketProtocol makes it simple to send arbitrary sized data "packets" across + fragmented transports such as TCP and UDP. + + As transmission boundaries are not respected, sending packets over protocols + like TCP frequently involves "stitching" them back together at the receiver. + QPacketProtocol makes this easier by performing this task for you. Packet + data sent using QPacketProtocol is prepended with a 4-byte size header + allowing the receiving QPacketProtocol to buffer the packet internally until + it has all been received. QPacketProtocol does not perform any sanity + checking on the size or on the data, so this class should only be used in + prototyping or trusted situations where DOS attacks are unlikely. + + QPacketProtocol does not perform any communications itself. Instead it can + operate on any QIODevice that supports the QIODevice::readyRead() signal. A + logical "packet" is encapsulated by the companion QPacket class. The + following example shows two ways to send data using QPacketProtocol. The + transmitted data is equivalent in both. + + \code + QTcpSocket socket; + // ... connect socket ... + + QPacketProtocol protocol(&socket); + + // Send a packet + QPacket packet; + packet << "Hello world" << 123; + protocol.send(packet); + \endcode + + Likewise, the following shows how to read data from QPacketProtocol, assuming + that the QPacketProtocol::readyRead() signal has been emitted. + + \code + // ... QPacketProtocol::readyRead() is emitted ... + + int a; + QByteArray b; + + // Receive packet the quick way + protocol.read() >> a >> b; + + // Receive packet the longer way + QPacket packet = protocol.read(); + p >> a >> b; + \endcode + + \ingroup io + \sa QPacket +*/ + +class QPacketProtocolPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QPacketProtocol) +public: + QPacketProtocolPrivate(QIODevice *dev); + + QList<qint64> sendingPackets; + QList<QByteArray> packets; + QByteArray inProgress; + qint32 inProgressSize; + bool waitingForPacket; + QIODevice *dev; +}; + +/*! + Construct a QPacketProtocol instance that works on \a dev with the + specified \a parent. + */ +QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent) + : QObject(*(new QPacketProtocolPrivate(dev)), parent) +{ + Q_ASSERT(4 == sizeof(qint32)); + Q_ASSERT(dev); + + QObject::connect(dev, SIGNAL(readyRead()), + this, SLOT(readyToRead())); + QObject::connect(dev, SIGNAL(aboutToClose()), + this, SLOT(aboutToClose())); + QObject::connect(dev, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64))); +} + +/*! + \fn void QPacketProtocol::send(const QPacket & packet) + + Transmit the \a packet. + */ +void QPacketProtocol::send(const QPacket & p) +{ + Q_D(QPacketProtocol); + + QByteArray data = p.data(); + if (data.isEmpty()) + return; // We don't send empty packets + qint64 sendSize = data.size() + sizeof(qint32); + + d->sendingPackets.append(sendSize); + qint32 sendSize32 = sendSize; + qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32)); + Q_UNUSED(writeBytes); + Q_ASSERT(writeBytes == sizeof(qint32)); + writeBytes = d->dev->write(data); + Q_ASSERT(writeBytes == data.size()); +} + +/*! + Returns the number of received packets yet to be read. + */ +qint64 QPacketProtocol::packetsAvailable() const +{ + Q_D(const QPacketProtocol); + return d->packets.count(); +} + +/*! + Return the next unread packet, or an invalid QPacket instance if no packets + are available. This method does NOT block. + */ +QPacket QPacketProtocol::read() +{ + Q_D(QPacketProtocol); + + // Hope for in-place construction here, until we get move semantics for QBuffer + return QPacket(d->packets.isEmpty() ? QByteArray() : d->packets.takeFirst()); +} + +/*! + This function locks until a new packet is available for reading and the + \l{QIODevice::}{readyRead()} signal has been emitted. The function + will timeout after \a msecs milliseconds; the default timeout is + 30000 milliseconds. + + The function returns true if the readyRead() signal is emitted and + there is new data available for reading; otherwise it returns false + (if an error occurred or the operation timed out). + */ + +bool QPacketProtocol::waitForReadyRead(int msecs) +{ + Q_D(QPacketProtocol); + if (!d->packets.isEmpty()) + return true; + + QElapsedTimer stopWatch; + stopWatch.start(); + + d->waitingForPacket = true; + do { + if (!d->dev->waitForReadyRead(msecs)) + return false; + if (!d->waitingForPacket) + return true; + msecs = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); + } while (true); +} + +/*! + Return the QIODevice passed to the QPacketProtocol constructor. +*/ +void QPacketProtocol::aboutToClose() +{ + Q_D(QPacketProtocol); + d->inProgress.clear(); + d->sendingPackets.clear(); + d->inProgressSize = -1; +} + +void QPacketProtocol::bytesWritten(qint64 bytes) +{ + Q_D(QPacketProtocol); + Q_ASSERT(!d->sendingPackets.isEmpty()); + + while (bytes) { + if (d->sendingPackets.at(0) > bytes) { + d->sendingPackets[0] -= bytes; + bytes = 0; + } else { + bytes -= d->sendingPackets.at(0); + d->sendingPackets.removeFirst(); + } + } +} + +void QPacketProtocol::readyToRead() +{ + Q_D(QPacketProtocol); + while (true) { + // Need to get trailing data + if (-1 == d->inProgressSize) { + // We need a size header of sizeof(qint32) + if (sizeof(qint32) > (uint)d->dev->bytesAvailable()) + return; + + // Read size header + int read = d->dev->read((char *)&d->inProgressSize, sizeof(qint32)); + Q_ASSERT(read == sizeof(qint32)); + Q_UNUSED(read); + + // Check sizing constraints + if (d->inProgressSize > MAX_PACKET_SIZE) { + QObject::disconnect(d->dev, SIGNAL(readyRead()), + this, SLOT(readyToRead())); + QObject::disconnect(d->dev, SIGNAL(aboutToClose()), + this, SLOT(aboutToClose())); + QObject::disconnect(d->dev, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64))); + d->dev = 0; + emit invalidPacket(); + return; + } + + d->inProgressSize -= sizeof(qint32); + } else { + d->inProgress.append(d->dev->read(d->inProgressSize - d->inProgress.size())); + + if (d->inProgressSize == d->inProgress.size()) { + // Packet has arrived! + d->packets.append(d->inProgress); + d->inProgressSize = -1; + d->inProgress.clear(); + + d->waitingForPacket = false; + emit readyRead(); + } else + return; + } + } +} + +QPacketProtocolPrivate::QPacketProtocolPrivate(QIODevice *dev) : + inProgressSize(-1), waitingForPacket(false), dev(dev) +{ +} + +/*! + \fn void QPacketProtocol::readyRead() + + Emitted whenever a new packet is received. Applications may use + QPacketProtocol::read() to retrieve this packet. + */ + +/*! + \fn void QPacketProtocol::invalidPacket() + + A packet larger than the maximum allowable packet size was received. The + packet will be discarded and, as it indicates corruption in the protocol, no + further packets will be received. + */ + +QT_END_NAMESPACE diff --git a/tools/qmlprofiler/qpacketprotocol.h b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h index 17608033f4..6ed803809f 100644 --- a/tools/qmlprofiler/qpacketprotocol.h +++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h @@ -31,79 +31,50 @@ ** ****************************************************************************/ -#ifndef QPACKETPROTOCOL_H -#define QPACKETPROTOCOL_H +#ifndef QPACKETPROTOCOL_P_H +#define QPACKETPROTOCOL_P_H #include <QtCore/qobject.h> -#include <QtCore/qdatastream.h> +#include <private/qpacket_p.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// QT_BEGIN_NAMESPACE + class QIODevice; -class QBuffer; -QT_END_NAMESPACE -class QPacket; -class QPacketAutoSend; -class QPacketProtocolPrivate; +class QPacketProtocolPrivate; class QPacketProtocol : public QObject { Q_OBJECT + Q_DECLARE_PRIVATE(QPacketProtocol) public: explicit QPacketProtocol(QIODevice *dev, QObject *parent = 0); - virtual ~QPacketProtocol(); - - qint32 maximumPacketSize() const; - qint32 setMaximumPacketSize(qint32); - QPacketAutoSend send(); void send(const QPacket &); - qint64 packetsAvailable() const; QPacket read(); - bool waitForReadyRead(int msecs = 3000); - void clear(); - - QIODevice *device(); - Q_SIGNALS: void readyRead(); void invalidPacket(); - void packetWritten(); -private: - QPacketProtocolPrivate *d; +private Q_SLOTS: + void aboutToClose(); + void bytesWritten(qint64 bytes); + void readyToRead(); }; +QT_END_NAMESPACE -class QPacket : public QDataStream -{ -public: - QPacket(); - QPacket(const QPacket &); - virtual ~QPacket(); - - void clear(); - bool isEmpty() const; - QByteArray data() const; - -protected: - friend class QPacketProtocol; - QPacket(const QByteArray &ba); - QByteArray b; - QBuffer *buf; -}; - -class QPacketAutoSend : public QPacket -{ -public: - virtual ~QPacketAutoSend(); - -private: - friend class QPacketProtocol; - QPacketAutoSend(QPacketProtocol *); - QPacketProtocol *p; -}; - -#endif +#endif // QPACKETPROTOCOL_P_H diff --git a/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.cpp index ee1047d2b0..e7b9f24ae7 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.cpp @@ -33,6 +33,7 @@ #include "qdebugmessageservice.h" #include <private/qqmldebugconnector_p.h> +#include <private/qpacket_p.h> #include <QDataStream> @@ -63,13 +64,12 @@ void QDebugMessageServiceImpl::sendDebugMessage(QtMsgType type, //We do not want to alter the message handling mechanism //We just eavesdrop and forward the messages to a port //only if a client is connected to it. - QByteArray message; - QQmlDebugStream ws(&message, QIODevice::WriteOnly); + QPacket ws; ws << QByteArray("MESSAGE") << type << buf.toUtf8(); ws << QString::fromLatin1(ctxt.file).toUtf8(); ws << ctxt.line << QString::fromLatin1(ctxt.function).toUtf8(); - emit messageToClient(name(), message); + emit messageToClient(name(), ws.data()); if (oldMsgHandler) (*oldMsgHandler)(type, ctxt, buf); } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index 8f53dc6d50..09c352e8ad 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -45,6 +45,7 @@ #include <private/qqmlvaluetype_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlexpression_p.h> +#include <private/qpacket_p.h> #include <QtCore/qdebug.h> #include <QtCore/qmetaobject.h> @@ -440,14 +441,13 @@ QList<QObject*> QQmlEngineDebugServiceImpl::objectForLocationInfo(const QString void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message) { - QQmlDebugStream ds(message); + QPacket ds(message); QByteArray type; int queryId; ds >> type >> queryId; - QByteArray reply; - QQmlDebugStream rs(&reply, QIODevice::WriteOnly); + QPacket rs; if (type == "LIST_ENGINES") { rs << QByteArray("LIST_ENGINES_R"); @@ -617,7 +617,7 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message) rs << QByteArray("SET_METHOD_BODY_R") << queryId << ok; } - emit messageToClient(name(), reply); + emit messageToClient(name(), rs.data()); } bool QQmlEngineDebugServiceImpl::setBinding(int objectId, @@ -769,12 +769,9 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth void QQmlEngineDebugServiceImpl::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value) { - QByteArray reply; - QQmlDebugStream rs(&reply, QIODevice::WriteOnly); - + QPacket rs; rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value); - - emit messageToClient(name(), reply); + emit messageToClient(name(), rs.data()); } void QQmlEngineDebugServiceImpl::engineAboutToBeAdded(QQmlEngine *engine) @@ -804,12 +801,11 @@ void QQmlEngineDebugServiceImpl::objectCreated(QQmlEngine *engine, QObject *obje int objectId = QQmlDebugService::idForObject(object); int parentId = QQmlDebugService::idForObject(object->parent()); - QByteArray reply; - QQmlDebugStream rs(&reply, QIODevice::WriteOnly); + QPacket rs; //unique queryId -1 rs << QByteArray("OBJECT_CREATED") << -1 << engineId << objectId << parentId; - emit messageToClient(name(), reply); + emit messageToClient(name(), rs.data()); } void QQmlEngineDebugServiceImpl::setStatesDelegate(QQmlDebugStatesDelegate *delegate) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index bedabd0a63..14333e01f6 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -36,7 +36,7 @@ #include <private/qv4engine_p.h> #include <private/qv4function_p.h> #include <private/qqmldebugconnector_p.h> - +#include <private/qpacket_p.h> #include <private/qv8engine_p.h> #include <QtCore/QJsonArray> @@ -674,7 +674,7 @@ void QV4DebugServiceImpl::messageReceived(const QByteArray &message) { QMutexLocker lock(&m_configMutex); - QQmlDebugStream ms(message); + QPacket ms(message); QByteArray header; ms >> header; @@ -715,11 +715,10 @@ void QV4DebugServiceImpl::messageReceived(const QByteArray &message) void QV4DebugServiceImpl::sendSomethingToSomebody(const char *type, int magicNumber) { - QByteArray response; - QQmlDebugStream rs(&response, QIODevice::WriteOnly); + QPacket rs; rs << QByteArray(type) << QByteArray::number(int(version())) << QByteArray::number(magicNumber); - emit messageToClient(name(), packMessage(type, response)); + emit messageToClient(name(), packMessage(type, rs.data())); } void QV4DebugServiceImpl::handleV8Request(const QByteArray &payload) @@ -739,11 +738,10 @@ void QV4DebugServiceImpl::handleV8Request(const QByteArray &payload) QByteArray QV4DebugServiceImpl::packMessage(const QByteArray &command, const QByteArray &message) { - QByteArray reply; - QQmlDebugStream rs(&reply, QIODevice::WriteOnly); + QPacket rs; static const QByteArray cmd("V8DEBUG"); rs << cmd << command << message; - return reply; + return rs.data(); } void QV4DebugServiceImpl::send(QJsonObject v8Payload) diff --git a/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.cpp index fa6dca7aca..d1bba54969 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.cpp @@ -40,6 +40,7 @@ #include <QtCore/private/qabstractanimation_p.h> #include <QtQml/private/qqmldebugconnector_p.h> #include <QtQml/private/qqmlcontext_p.h> +#include <QtQml/private/qpacket_p.h> #include <QtGui/QMouseEvent> #include <QtGui/QTouchEvent> @@ -262,18 +263,16 @@ void AbstractViewInspector::onQmlObjectDestroyed(QObject *object) QPair<int, int> ids = m_hashObjectsTobeDestroyed.take(object); - QByteArray response; - - QQmlDebugStream rs(&response, QIODevice::WriteOnly); + QPacket rs; rs << QByteArray(RESPONSE) << ids.first << true << ids.second; - emit m_debugService->messageToClient(m_debugService->name(), response); + emit m_debugService->messageToClient(m_debugService->name(), rs.data()); } void AbstractViewInspector::handleMessage(const QByteArray &message) { bool success = true; - QQmlDebugStream ds(message); + QPacket ds(message); QByteArray type; ds >> type; @@ -358,16 +357,14 @@ void AbstractViewInspector::handleMessage(const QByteArray &message) } - QByteArray response; - QQmlDebugStream rs(&response, QIODevice::WriteOnly); + QPacket rs; rs << QByteArray(RESPONSE) << requestId << success; - emit m_debugService->messageToClient(m_debugService->name(), response); + emit m_debugService->messageToClient(m_debugService->name(), rs.data()); } void AbstractViewInspector::sendCurrentObjects(const QList<QObject*> &objects) { - QByteArray message; - QQmlDebugStream ds(&message, QIODevice::WriteOnly); + QPacket ds; ds << QByteArray(EVENT) << m_eventId++ << QByteArray(SELECT); @@ -377,7 +374,7 @@ void AbstractViewInspector::sendCurrentObjects(const QList<QObject*> &objects) debugIds << QQmlDebugService::idForObject(object); ds << debugIds; - emit m_debugService->messageToClient(m_debugService->name(), message); + emit m_debugService->messageToClient(m_debugService->name(), ds.data()); } void AbstractViewInspector::sendQmlFileReloaded(bool success) @@ -387,10 +384,10 @@ void AbstractViewInspector::sendQmlFileReloaded(bool success) QByteArray response; - QQmlDebugStream rs(&response, QIODevice::WriteOnly); + QPacket rs; rs << QByteArray(RESPONSE) << m_reloadEventId << success; - emit m_debugService->messageToClient(m_debugService->name(), response); + emit m_debugService->messageToClient(m_debugService->name(), rs.data()); } QString AbstractViewInspector::idStringForObject(QObject *obj) const diff --git a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp index 057bf9523e..478cbf5514 100644 --- a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp @@ -32,7 +32,6 @@ ****************************************************************************/ #include "qlocalclientconnectionfactory.h" -#include "qpacketprotocol.h" #include "qqmldebugserver.h" #include <QtCore/qplugin.h> diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp index 8f697f1571..b4dfa86e56 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp @@ -33,6 +33,7 @@ #include "qqmlenginecontrolservice.h" #include <QQmlEngine> +#include <private/qpacket_p.h> QT_BEGIN_NAMESPACE @@ -44,7 +45,7 @@ QQmlEngineControlServiceImpl::QQmlEngineControlServiceImpl(QObject *parent) : void QQmlEngineControlServiceImpl::messageReceived(const QByteArray &message) { QMutexLocker lock(&dataMutex); - QQmlDebugStream d(message); + QPacket d(message); int command; int engineId; d >> command >> engineId; @@ -106,10 +107,9 @@ void QQmlEngineControlServiceImpl::engineRemoved(QQmlEngine *engine) void QQmlEngineControlServiceImpl::sendMessage(QQmlEngineControlServiceImpl::MessageType type, QQmlEngine *engine) { - QByteArray message; - QQmlDebugStream d(&message, QIODevice::WriteOnly); + QPacket d; d << type << idForObject(engine); - emit messageToClient(name(), message); + emit messageToClient(name(), d.data()); } void QQmlEngineControlServiceImpl::stateChanged(State) diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp index 245900abae..8879cc4037 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp @@ -33,6 +33,7 @@ #include "qqmlprofileradapter.h" #include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qpacket_p.h> QT_BEGIN_NAMESPACE @@ -72,7 +73,7 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData *d, QList<QByteA continue; //### using QDataStream is relatively expensive - QQmlDebugStream ds(&data, QIODevice::WriteOnly); + QPacket ds; ds << d->time << decodedMessageType << decodedDetailType; switch (decodedMessageType) { @@ -92,8 +93,7 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData *d, QList<QByteA Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type."); break; } - messages << data; - data.clear(); + messages << ds.data(); } } } diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp index 65b99ef7ca..2ce5610c7e 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp @@ -36,6 +36,7 @@ #include "qqmlprofileradapter.h" #include "qqmlprofilerservicefactory.h" #include <private/qqmlengine_p.h> +#include <private/qpacket_p.h> #include <QtCore/qdatastream.h> #include <QtCore/qurl.h> @@ -202,8 +203,7 @@ void QQmlProfilerServiceImpl::startProfiling(QQmlEngine *engine, quint64 feature { QMutexLocker lock(&m_configMutex); - QByteArray message; - QQmlDebugStream d(&message, QIODevice::WriteOnly); + QPacket d; d << m_timer.nsecsElapsed() << (int)Event << (int)StartTrace; bool startedAny = false; @@ -239,7 +239,7 @@ void QQmlProfilerServiceImpl::startProfiling(QQmlEngine *engine, quint64 feature emit startFlushTimer(); } - emit messageToClient(name(), message); + emit messageToClient(name(), d.data()); } /*! @@ -299,10 +299,8 @@ void QQmlProfilerServiceImpl::sendMessages() { QList<QByteArray> messages; - QByteArray data; - + QPacket traceEnd; if (m_waitingForStop) { - QQmlDebugStream traceEnd(&data, QIODevice::WriteOnly); traceEnd << m_timer.nsecsElapsed() << (int)Event << (int)EndTrace; QSet<QQmlEngine *> seen; @@ -331,12 +329,11 @@ void QQmlProfilerServiceImpl::sendMessages() if (m_waitingForStop) { //indicate completion - messages << data; - data.clear(); + messages << traceEnd.data(); - QQmlDebugStream ds(&data, QIODevice::WriteOnly); + QPacket ds; ds << (qint64)-1 << (int)Complete; - messages << data; + messages << ds.data(); m_waitingForStop = false; } @@ -369,8 +366,7 @@ void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message) { QMutexLocker lock(&m_configMutex); - QByteArray rwData = message; - QQmlDebugStream stream(&rwData, QIODevice::ReadOnly); + QPacket stream(message); int engineId = -1; quint64 features = std::numeric_limits<quint64>::max(); diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp index 3498555f65..991ef4b6ba 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp @@ -34,6 +34,8 @@ #include "qv4profileradapter.h" #include "qqmlprofilerservice.h" +#include <private/qpacket_p.h> + QT_BEGIN_NAMESPACE QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::ExecutionEngine *engine) : @@ -62,13 +64,12 @@ QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::Execut qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList<QByteArray> &messages) { - QByteArray message; while (m_memoryData.length() > m_memoryPos && m_memoryData[m_memoryPos].timestamp <= until) { - QQmlDebugStream d(&message, QIODevice::WriteOnly); + QPacket d; QV4::Profiling::MemoryAllocationProperties &props = m_memoryData[m_memoryPos]; d << props.timestamp << MemoryAllocation << props.type << props.size; ++m_memoryPos; - messages.append(message); + messages.append(d.data()); } return m_memoryData.length() == m_memoryPos ? -1 : m_memoryData[m_memoryPos].timestamp; } @@ -94,7 +95,6 @@ qint64 QV4ProfilerAdapter::finalizeMessages(qint64 until, QList<QByteArray> &mes qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) { - QByteArray message; while (true) { while (!m_stack.isEmpty() && (m_functionCallPos == m_functionCallData.length() || @@ -103,9 +103,9 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message return finalizeMessages(until, messages, m_stack.top()); appendMemoryEvents(m_stack.top(), messages); - QQmlDebugStream d(&message, QIODevice::WriteOnly); + QPacket d; d << m_stack.pop() << RangeEnd << Javascript; - messages.append(message); + messages.append(d.data()); } while (m_functionCallPos != m_functionCallData.length() && (m_stack.empty() || m_functionCallData[m_functionCallPos].start < m_stack.top())) { @@ -116,19 +116,16 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message appendMemoryEvents(props.start, messages); - QQmlDebugStream d_start(&message, QIODevice::WriteOnly); + QPacket d_start; d_start << props.start << RangeStart << Javascript; - messages.push_back(message); - message.clear(); - QQmlDebugStream d_location(&message, QIODevice::WriteOnly); + messages.push_back(d_start.data()); + QPacket d_location; d_location << props.start << RangeLocation << Javascript << props.file << props.line << props.column; - messages.push_back(message); - message.clear(); - QQmlDebugStream d_data(&message, QIODevice::WriteOnly); + messages.push_back(d_location.data()); + QPacket d_data; d_data << props.start << RangeData << Javascript << props.name; - messages.push_back(message); - message.clear(); + messages.push_back(d_data.data()); m_stack.push(props.end); ++m_functionCallPos; } diff --git a/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro b/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro index 5e2d0874df..7347a7598f 100644 --- a/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro +++ b/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro @@ -1,5 +1,5 @@ TARGET = qmldbg_server -QT = qml-private core-private +QT = qml-private packetprotocol-private PLUGIN_TYPE = qmltooling PLUGIN_CLASS_NAME = QQmlDebugServerFactory @@ -7,12 +7,10 @@ load(qt_plugin) SOURCES += \ $$PWD/qqmldebugserver.cpp \ - $$PWD/../shared/qpacketprotocol.cpp HEADERS += \ $$PWD/qqmldebugserverfactory.h \ $$PWD/../shared/qqmldebugserver.h \ - $$PWD/../shared/qpacketprotocol.h \ $$PWD/../shared/qqmldebugserverconnection.h INCLUDEPATH += $$PWD \ diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp index d3a8d1f0d1..bfbeef5856 100644 --- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp +++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp @@ -33,7 +33,6 @@ #include "qqmldebugserver.h" #include "qqmldebugserverfactory.h" -#include "qpacketprotocol.h" #include "qqmldebugserverconnection.h" #include <private/qqmldebugservice_p.h> @@ -41,6 +40,8 @@ #include <private/qqmlglobal_p.h> #include <private/qqmldebugpluginmanager_p.h> #include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qpacketprotocol_p.h> +#include <private/qpacket_p.h> #include <QtCore/QAtomicInt> #include <QtCore/QDir> @@ -48,9 +49,6 @@ #include <QtCore/QStringList> #include <QtCore/qwaitcondition.h> -#include <private/qobject_p.h> -#include <private/qcoreapplication_p.h> - QT_BEGIN_NAMESPACE /* @@ -431,7 +429,7 @@ void QQmlDebugServerImpl::receiveMessage() if (!m_protocol) return; - QQmlDebugStream in(m_protocol->read().data()); + QPacket in(m_protocol->read().data()); QString name; @@ -445,16 +443,17 @@ void QQmlDebugServerImpl::receiveMessage() //Get the supported QDataStream version if (!in.atEnd()) { - in >> QQmlDebugStream::s_dataStreamVersion; - if (QQmlDebugStream::s_dataStreamVersion > QDataStream().version()) - QQmlDebugStream::s_dataStreamVersion = QDataStream().version(); + int dataStreamVersion; + in >> dataStreamVersion; + if (dataStreamVersion > QDataStream().version()) + dataStreamVersion = QDataStream().version(); + QPacket::setDataStreamVersion(dataStreamVersion); } // Send the hello answer immediately, since it needs to arrive before // the plugins below start sending messages. - QByteArray helloAnswer; - QQmlDebugStream out(&helloAnswer, QIODevice::WriteOnly); + QPacket out; QStringList pluginNames; QList<float> pluginVersions; const int count = m_plugins.count(); @@ -467,11 +466,9 @@ void QQmlDebugServerImpl::receiveMessage() } out << QString(QStringLiteral("QDeclarativeDebugClient")) << 0 << protocolVersion - << pluginNames << pluginVersions << QQmlDebugStream::s_dataStreamVersion; + << pluginNames << pluginVersions << QPacket::dataStreamVersion(); - QPacket pack; - pack.writeRawData(helloAnswer.data(), helloAnswer.length()); - m_protocol->send(pack); + m_protocol->send(out); m_connection->flush(); QMutexLocker helloLock(&m_helloMutex); @@ -653,13 +650,9 @@ bool QQmlDebugServerImpl::canSendMessage(const QString &name) void QQmlDebugServerImpl::doSendMessage(const QString &name, const QByteArray &message) { - QByteArray prefixed; - QQmlDebugStream out(&prefixed, QIODevice::WriteOnly); + QPacket out; out << name << message; - - QPacket pack; - pack.writeRawData(prefixed.data(), prefixed.length()); - m_protocol->send(pack); + m_protocol->send(out); } void QQmlDebugServerImpl::sendMessage(const QString &name, const QByteArray &message) diff --git a/src/plugins/qmltooling/qmltooling.pro b/src/plugins/qmltooling/qmltooling.pro index e00e75599b..75da89f3e8 100644 --- a/src/plugins/qmltooling/qmltooling.pro +++ b/src/plugins/qmltooling/qmltooling.pro @@ -8,7 +8,10 @@ SUBDIRS += \ # Services SUBDIRS += \ + packetprotocol \ qmldbg_debugger \ qmldbg_profiler +qmldbg_server.depends = packetprotocol + qtHaveModule(quick): SUBDIRS += qmldbg_inspector diff --git a/src/plugins/qmltooling/shared/qpacketprotocol.cpp b/src/plugins/qmltooling/shared/qpacketprotocol.cpp deleted file mode 100644 index 9a58f803c1..0000000000 --- a/src/plugins/qmltooling/shared/qpacketprotocol.cpp +++ /dev/null @@ -1,531 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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. -** -** 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. -** -** 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. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qpacketprotocol.h" - -#include <QtCore/QBuffer> -#include <QtCore/QElapsedTimer> -#include <private/qiodevice_p.h> - -QT_BEGIN_NAMESPACE - -static const unsigned int MAX_PACKET_SIZE = 0x7FFFFFFF; - -/*! - \class QPacketProtocol - \internal - - \brief The QPacketProtocol class encapsulates communicating discrete packets - across fragmented IO channels, such as TCP sockets. - - QPacketProtocol makes it simple to send arbitrary sized data "packets" across - fragmented transports such as TCP and UDP. - - As transmission boundaries are not respected, sending packets over protocols - like TCP frequently involves "stitching" them back together at the receiver. - QPacketProtocol makes this easier by performing this task for you. Packet - data sent using QPacketProtocol is prepended with a 4-byte size header - allowing the receiving QPacketProtocol to buffer the packet internally until - it has all been received. QPacketProtocol does not perform any sanity - checking on the size or on the data, so this class should only be used in - prototyping or trusted situations where DOS attacks are unlikely. - - QPacketProtocol does not perform any communications itself. Instead it can - operate on any QIODevice that supports the QIODevice::readyRead() signal. A - logical "packet" is encapsulated by the companion QPacket class. The - following example shows two ways to send data using QPacketProtocol. The - transmitted data is equivalent in both. - - \code - QTcpSocket socket; - // ... connect socket ... - - QPacketProtocol protocol(&socket); - - // Send packet the quick way - protocol.send() << "Hello world" << 123; - - // Send packet the longer way - QPacket packet; - packet << "Hello world" << 123; - protocol.send(packet); - \endcode - - Likewise, the following shows how to read data from QPacketProtocol, assuming - that the QPacketProtocol::readyRead() signal has been emitted. - - \code - // ... QPacketProtocol::readyRead() is emitted ... - - int a; - QByteArray b; - - // Receive packet the quick way - protocol.read() >> a >> b; - - // Receive packet the longer way - QPacket packet = protocol.read(); - p >> a >> b; - \endcode - - \ingroup io - \sa QPacket -*/ - -class QPacketProtocolPrivate : public QObject -{ - Q_OBJECT -public: - QPacketProtocolPrivate(QPacketProtocol *parent, QIODevice *_dev) - : QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE), - waitingForPacket(false), dev(_dev) - { - Q_ASSERT(4 == sizeof(qint32)); - - QObject::connect(this, SIGNAL(readyRead()), - parent, SIGNAL(readyRead())); - QObject::connect(this, SIGNAL(packetWritten()), - parent, SIGNAL(packetWritten())); - QObject::connect(this, SIGNAL(invalidPacket()), - parent, SIGNAL(invalidPacket())); - QObject::connect(dev, SIGNAL(readyRead()), - this, SLOT(readyToRead())); - QObject::connect(dev, SIGNAL(aboutToClose()), - this, SLOT(aboutToClose())); - QObject::connect(dev, SIGNAL(bytesWritten(qint64)), - this, SLOT(bytesWritten(qint64))); - } - -Q_SIGNALS: - void readyRead(); - void packetWritten(); - void invalidPacket(); - -public Q_SLOTS: - void aboutToClose() - { - inProgress.clear(); - sendingPackets.clear(); - inProgressSize = -1; - } - - void bytesWritten(qint64 bytes) - { - Q_ASSERT(!sendingPackets.isEmpty()); - - while (bytes) { - if (sendingPackets.at(0) > bytes) { - sendingPackets[0] -= bytes; - bytes = 0; - } else { - bytes -= sendingPackets.at(0); - sendingPackets.removeFirst(); - emit packetWritten(); - } - } - } - - void readyToRead() - { - while (true) { - // Need to get trailing data - if (-1 == inProgressSize) { - // We need a size header of sizeof(qint32) - if (sizeof(qint32) > (uint)dev->bytesAvailable()) - return; - - // Read size header - int read = dev->read((char *)&inProgressSize, sizeof(qint32)); - Q_ASSERT(read == sizeof(qint32)); - Q_UNUSED(read); - - // Check sizing constraints - if (inProgressSize > maxPacketSize) { - QObject::disconnect(dev, SIGNAL(readyRead()), - this, SLOT(readyToRead())); - QObject::disconnect(dev, SIGNAL(aboutToClose()), - this, SLOT(aboutToClose())); - QObject::disconnect(dev, SIGNAL(bytesWritten(qint64)), - this, SLOT(bytesWritten(qint64))); - dev = 0; - emit invalidPacket(); - return; - } - - inProgressSize -= sizeof(qint32); - } else { - inProgress.append(dev->read(inProgressSize - inProgress.size())); - - if (inProgressSize == inProgress.size()) { - // Packet has arrived! - packets.append(inProgress); - inProgressSize = -1; - inProgress.clear(); - - waitingForPacket = false; - emit readyRead(); - } else - return; - } - } - } - -public: - QList<qint64> sendingPackets; - QList<QByteArray> packets; - QByteArray inProgress; - qint32 inProgressSize; - qint32 maxPacketSize; - bool waitingForPacket; - QIODevice *dev; -}; - -/*! - Construct a QPacketProtocol instance that works on \a dev with the - specified \a parent. - */ -QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent) - : QObject(parent), d(new QPacketProtocolPrivate(this, dev)) -{ - Q_ASSERT(dev); -} - -/*! - Destroys the QPacketProtocol instance. - */ -QPacketProtocol::~QPacketProtocol() -{ -} - -/*! - Returns the maximum packet size allowed. By default this is - 2,147,483,647 bytes. - - If a packet claiming to be larger than the maximum packet size is received, - the QPacketProtocol::invalidPacket() signal is emitted. - - \sa QPacketProtocol::setMaximumPacketSize() - */ -qint32 QPacketProtocol::maximumPacketSize() const -{ - return d->maxPacketSize; -} - -/*! - Sets the maximum allowable packet size to \a max. - - \sa QPacketProtocol::maximumPacketSize() - */ -qint32 QPacketProtocol::setMaximumPacketSize(qint32 max) -{ - if (max > (signed)sizeof(qint32)) - d->maxPacketSize = max; - return d->maxPacketSize; -} - -/*! - Returns a streamable object that is transmitted on destruction. For example - - \code - protocol.send() << "Hello world" << 123; - \endcode - - will send a packet containing "Hello world" and 123. To construct more - complex packets, explicitly construct a QPacket instance. - */ -QPacketAutoSend QPacketProtocol::send() -{ - return QPacketAutoSend(this); -} - -/*! - \fn void QPacketProtocol::send(const QPacket & packet) - - Transmit the \a packet. - */ -void QPacketProtocol::send(const QPacket & p) -{ - if (p.b.isEmpty()) - return; // We don't send empty packets - - qint64 sendSize = p.b.size() + sizeof(qint32); - - d->sendingPackets.append(sendSize); - qint32 sendSize32 = sendSize; - qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32)); - Q_UNUSED(writeBytes); - Q_ASSERT(writeBytes == sizeof(qint32)); - writeBytes = d->dev->write(p.b); - Q_ASSERT(writeBytes == p.b.size()); -} - -/*! - Returns the number of received packets yet to be read. - */ -qint64 QPacketProtocol::packetsAvailable() const -{ - return d->packets.count(); -} - -/*! - Discard any unread packets. - */ -void QPacketProtocol::clear() -{ - d->packets.clear(); -} - -/*! - Return the next unread packet, or an invalid QPacket instance if no packets - are available. This method does NOT block. - */ -QPacket QPacketProtocol::read() -{ - if (0 == d->packets.count()) - return QPacket(); - - QPacket rv(d->packets.at(0)); - d->packets.removeFirst(); - return rv; -} - -/*! - This function locks until a new packet is available for reading and the - \l{QIODevice::}{readyRead()} signal has been emitted. The function - will timeout after \a msecs milliseconds; the default timeout is - 30000 milliseconds. - - The function returns true if the readyRead() signal is emitted and - there is new data available for reading; otherwise it returns false - (if an error occurred or the operation timed out). - */ - -bool QPacketProtocol::waitForReadyRead(int msecs) -{ - if (!d->packets.isEmpty()) - return true; - - QElapsedTimer stopWatch; - stopWatch.start(); - - d->waitingForPacket = true; - do { - if (!d->dev->waitForReadyRead(msecs)) - return false; - if (!d->waitingForPacket) - return true; - msecs = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); - } while (true); -} - -/*! - Return the QIODevice passed to the QPacketProtocol constructor. -*/ -QIODevice *QPacketProtocol::device() -{ - return d->dev; -} - -/*! - \fn void QPacketProtocol::readyRead() - - Emitted whenever a new packet is received. Applications may use - QPacketProtocol::read() to retrieve this packet. - */ - -/*! - \fn void QPacketProtocol::invalidPacket() - - A packet larger than the maximum allowable packet size was received. The - packet will be discarded and, as it indicates corruption in the protocol, no - further packets will be received. - */ - -/*! - \fn void QPacketProtocol::packetWritten() - - Emitted each time a packet is completing written to the device. This signal - may be used for communications flow control. - */ - -/*! - \class QPacket - \internal - - \brief The QPacket class encapsulates an unfragmentable packet of data to be - transmitted by QPacketProtocol. - - The QPacket class works together with QPacketProtocol to make it simple to - send arbitrary sized data "packets" across fragmented transports such as TCP - and UDP. - - QPacket provides a QDataStream interface to an unfragmentable packet. - Applications should construct a QPacket, propagate it with data and then - transmit it over a QPacketProtocol instance. For example: - \code - QPacketProtocol protocol(...); - - QPacket myPacket; - myPacket << "Hello world!" << 123; - protocol.send(myPacket); - \endcode - - As long as both ends of the connection are using the QPacketProtocol class, - the data within this packet will be delivered unfragmented at the other end, - ready for extraction. - - \code - QByteArray greeting; - int count; - - QPacket myPacket = protocol.read(); - - myPacket >> greeting >> count; - \endcode - - Only packets returned from QPacketProtocol::read() may be read from. QPacket - instances constructed by directly by applications are for transmission only - and are considered "write only". Attempting to read data from them will - result in undefined behavior. - - \ingroup io - \sa QPacketProtocol - */ - -/*! - Constructs an empty write-only packet. - */ -QPacket::QPacket() - : QDataStream(), buf(0) -{ - buf = new QBuffer(&b); - buf->open(QIODevice::WriteOnly); - setDevice(buf); - setVersion(QDataStream::Qt_4_7); -} - -/*! - Destroys the QPacket instance. - */ -QPacket::~QPacket() -{ - if (buf) { - delete buf; - buf = 0; - } -} - -/*! - Creates a copy of \a other. The initial stream positions are shared, but the - two packets are otherwise independent. - */ -QPacket::QPacket(const QPacket & other) - : QDataStream(), b(other.b), buf(0) -{ - buf = new QBuffer(&b); - buf->open(other.buf->openMode()); - setDevice(buf); -} - -/*! - \internal - */ -QPacket::QPacket(const QByteArray & ba) - : QDataStream(), b(ba), buf(0) -{ - buf = new QBuffer(&b); - buf->open(QIODevice::ReadOnly); - setDevice(buf); -} - -/*! - Returns true if this packet is empty - that is, contains no data. - */ -bool QPacket::isEmpty() const -{ - return b.isEmpty(); -} - -/*! - Returns raw packet data. - */ -QByteArray QPacket::data() const -{ - return b; -} - -/*! - Clears data in the packet. This is useful for reusing one writable packet. - For example - \code - QPacketProtocol protocol(...); - - QPacket packet; - - packet << "Hello world!" << 123; - protocol.send(packet); - - packet.clear(); - packet << "Goodbyte world!" << 789; - protocol.send(packet); - \endcode - */ -void QPacket::clear() -{ - QBuffer::OpenMode oldMode = buf->openMode(); - buf->close(); - b.clear(); - buf->setBuffer(&b); // reset QBuffer internals with new size of b. - buf->open(oldMode); -} - -/*! - \class QPacketAutoSend - \internal - - \internal - */ -QPacketAutoSend::QPacketAutoSend(QPacketProtocol *_p) - : QPacket(), p(_p) -{ -} - -QPacketAutoSend::~QPacketAutoSend() -{ - if (!b.isEmpty()) - p->send(*this); -} - -QT_END_NAMESPACE - -#include <qpacketprotocol.moc> diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri index 30a44eedd1..62b375f72f 100644 --- a/src/qml/debugger/debugger.pri +++ b/src/qml/debugger/debugger.pri @@ -6,7 +6,8 @@ SOURCES += \ $$PWD/qqmldebugservice.cpp \ $$PWD/qqmldebugserviceinterfaces.cpp \ $$PWD/qqmlabstractprofileradapter.cpp \ - $$PWD/qqmlprofiler.cpp + $$PWD/qqmlprofiler.cpp \ + $$PWD/qpacket.cpp HEADERS += \ $$PWD/qqmldebugconnector_p.h \ @@ -18,6 +19,7 @@ HEADERS += \ $$PWD/qqmldebug.h \ $$PWD/qqmlprofilerdefinitions_p.h \ $$PWD/qqmlabstractprofileradapter_p.h \ - $$PWD/qqmlprofiler_p.h + $$PWD/qqmlprofiler_p.h \ + $$PWD/qpacket_p.h INCLUDEPATH += $$PWD diff --git a/src/qml/debugger/qpacket.cpp b/src/qml/debugger/qpacket.cpp new file mode 100644 index 0000000000..30f2191689 --- /dev/null +++ b/src/qml/debugger/qpacket.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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. +** +** 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. +** +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpacket_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QPacket + \internal + + \brief The QPacket class encapsulates an unfragmentable packet of data to be + transmitted by QPacketProtocol. + + The QPacket class works together with QPacketProtocol to make it simple to + send arbitrary sized data "packets" across fragmented transports such as TCP + and UDP. + + QPacket provides a QDataStream interface to an unfragmentable packet. + Applications should construct a QPacket, propagate it with data and then + transmit it over a QPacketProtocol instance. For example: + \code + QPacketProtocol protocol(...); + + QPacket myPacket; + myPacket << "Hello world!" << 123; + protocol.send(myPacket); + \endcode + + As long as both ends of the connection are using the QPacketProtocol class, + the data within this packet will be delivered unfragmented at the other end, + ready for extraction. + + \code + QByteArray greeting; + int count; + + QPacket myPacket = protocol.read(); + + myPacket >> greeting >> count; + \endcode + + Only packets constructed from raw byte arrays may be read from. Empty QPacket + instances are for transmission only and are considered "write only". Attempting + to read data from them will result in undefined behavior. + + \ingroup io + \sa QPacketProtocol + */ + +int QPacket::s_dataStreamVersion = QDataStream::Qt_4_7; + +void QPacket::setDataStreamVersion(int dataStreamVersion) +{ + s_dataStreamVersion = dataStreamVersion; +} + +int QPacket::dataStreamVersion() +{ + return s_dataStreamVersion; +} + +/*! + Constructs an empty write-only packet. + */ +QPacket::QPacket() +{ + init(QIODevice::WriteOnly); +} + +/*! + Creates a copy of \a other. The initial stream positions are shared, but the + two packets are otherwise independent. + */ +QPacket::QPacket(const QPacket &other) : QDataStream() +{ + assign(other); +} + +QPacket &QPacket::operator=(const QPacket &other) +{ + if (this != &other) { + buf.close(); + assign(other); + } + return *this; +} + +QPacket::QPacket(const QByteArray &data) +{ + buf.setData(data); + init(QIODevice::ReadOnly); +} + +/*! + Returns raw packet data. + */ +QByteArray QPacket::data() const +{ + return buf.data(); +} + +void QPacket::init(QIODevice::OpenMode mode) +{ + buf.open(mode); + setDevice(&buf); + setVersion(s_dataStreamVersion); +} + +void QPacket::assign(const QPacket &other) +{ + buf.setData(other.buf.data()); + init(other.buf.openMode()); + buf.seek(other.buf.pos()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/shared/qpacketprotocol.h b/src/qml/debugger/qpacket_p.h index c571e8d2b8..759ed25145 100644 --- a/src/plugins/qmltooling/shared/qpacketprotocol.h +++ b/src/qml/debugger/qpacket_p.h @@ -31,81 +31,46 @@ ** ****************************************************************************/ -#ifndef QPACKETPROTOCOL_H -#define QPACKETPROTOCOL_H +#ifndef QPACKET_P_H +#define QPACKET_P_H -#include <QtCore/qobject.h> #include <QtCore/qdatastream.h> +#include <QtCore/qbuffer.h> +#include <QtQml/private/qtqmlglobal_p.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// QT_BEGIN_NAMESPACE -class QIODevice; -class QBuffer; -class QPacket; -class QPacketAutoSend; -class QPacketProtocolPrivate; - -class QPacketProtocol : public QObject -{ - Q_OBJECT -public: - explicit QPacketProtocol(QIODevice *dev, QObject *parent = 0); - virtual ~QPacketProtocol(); - - qint32 maximumPacketSize() const; - qint32 setMaximumPacketSize(qint32); - - QPacketAutoSend send(); - void send(const QPacket &); - - qint64 packetsAvailable() const; - QPacket read(); - - bool waitForReadyRead(int msecs = 3000); - - void clear(); - - QIODevice *device(); - -Q_SIGNALS: - void readyRead(); - void invalidPacket(); - void packetWritten(); - -private: - QPacketProtocolPrivate *d; -}; - - -class QPacket : public QDataStream +class Q_QML_PRIVATE_EXPORT QPacket : public QDataStream { public: QPacket(); - QPacket(const QPacket &); - virtual ~QPacket(); + QPacket(const QPacket &other); + explicit QPacket(const QByteArray &ba); + QPacket &operator=(const QPacket &other); - void clear(); - bool isEmpty() const; QByteArray data() const; -protected: - friend class QPacketProtocol; - QPacket(const QByteArray &ba); - QByteArray b; - QBuffer *buf; -}; - -class QPacketAutoSend : public QPacket -{ -public: - virtual ~QPacketAutoSend(); + static int dataStreamVersion(); + static void setDataStreamVersion(int dataStreamVersion); private: - friend class QPacketProtocol; - QPacketAutoSend(QPacketProtocol *); - QPacketProtocol *p; + static int s_dataStreamVersion; + void init(QIODevice::OpenMode mode); + void assign(const QPacket &other); + QBuffer buf; }; QT_END_NAMESPACE -#endif +#endif // QPACKET_P_H diff --git a/src/qml/debugger/qqmldebugservice.cpp b/src/qml/debugger/qqmldebugservice.cpp index 0b07f320ec..bbcf67a54b 100644 --- a/src/qml/debugger/qqmldebugservice.cpp +++ b/src/qml/debugger/qqmldebugservice.cpp @@ -200,32 +200,6 @@ void QQmlDebugService::engineRemoved(QQmlEngine *) { } -int QQmlDebugStream::s_dataStreamVersion = QDataStream::Qt_4_7; - -QQmlDebugStream::QQmlDebugStream() - : QDataStream() -{ - setVersion(s_dataStreamVersion); -} - -QQmlDebugStream::QQmlDebugStream(QIODevice *d) - : QDataStream(d) -{ - setVersion(s_dataStreamVersion); -} - -QQmlDebugStream::QQmlDebugStream(QByteArray *ba, QIODevice::OpenMode flags) - : QDataStream(ba, flags) -{ - setVersion(s_dataStreamVersion); -} - -QQmlDebugStream::QQmlDebugStream(const QByteArray &ba) - : QDataStream(ba) -{ - setVersion(s_dataStreamVersion); -} - QT_END_NAMESPACE #include "qqmldebugservice.moc" diff --git a/src/qml/debugger/qqmldebugservice_p.h b/src/qml/debugger/qqmldebugservice_p.h index 3d692133cc..8b58f57349 100644 --- a/src/qml/debugger/qqmldebugservice_p.h +++ b/src/qml/debugger/qqmldebugservice_p.h @@ -96,17 +96,6 @@ signals: void messagesToClient(const QString &name, const QList<QByteArray> &messages); }; -class Q_QML_PRIVATE_EXPORT QQmlDebugStream : public QDataStream -{ -public: - static int s_dataStreamVersion; - - QQmlDebugStream(); - explicit QQmlDebugStream(QIODevice *d); - QQmlDebugStream(QByteArray *ba, QIODevice::OpenMode flags); - QQmlDebugStream(const QByteArray &ba); -}; - QT_END_NAMESPACE #endif // QQMLDEBUGSERVICE_H diff --git a/src/quick/util/qquickprofiler.cpp b/src/quick/util/qquickprofiler.cpp index 77ffda474a..44fdbc5da8 100644 --- a/src/quick/util/qquickprofiler.cpp +++ b/src/quick/util/qquickprofiler.cpp @@ -34,6 +34,7 @@ #include "qquickprofiler_p.h" #include <QCoreApplication> #include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qpacket_p.h> QT_BEGIN_NAMESPACE @@ -46,7 +47,6 @@ quint64 QQuickProfiler::featuresEnabled = 0; // (see tst_qqmldebugtrace::trace() benchmark) void QQuickProfilerData::toByteArrays(QList<QByteArray> &messages) const { - QByteArray data; Q_ASSERT_X(((messageType | detailType) & (1 << 31)) == 0, Q_FUNC_INFO, "You can use at most 31 message types and 31 detail types."); for (uint decodedMessageType = 0; (messageType >> decodedMessageType) != 0; ++decodedMessageType) { if ((messageType & (1 << decodedMessageType)) == 0) @@ -57,7 +57,7 @@ void QQuickProfilerData::toByteArrays(QList<QByteArray> &messages) const continue; //### using QDataStream is relatively expensive - QQmlDebugStream ds(&data, QIODevice::WriteOnly); + QPacket ds; ds << time << decodedMessageType << decodedDetailType; switch (decodedMessageType) { @@ -103,8 +103,7 @@ void QQuickProfilerData::toByteArrays(QList<QByteArray> &messages) const Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type."); break; } - messages << data; - data.clear(); + messages << ds.data(); } } } diff --git a/sync.profile b/sync.profile index 28d0698da9..6695d98f27 100644 --- a/sync.profile +++ b/sync.profile @@ -5,6 +5,7 @@ "QtQuickParticles" => "$basedir/src/particles", "QtQuickTest" => "$basedir/src/qmltest", "QtQmlDevTools" => "$basedir/src/qmldevtools", + "QtPacketProtocol" => "$basedir/src/plugins/qmltooling/packetprotocol", ); %moduleheaders = ( # restrict the module headers to those found in relative path "QtQmlDevTools" => "../qml/parser;../qml/jsruntime;../qml/qml/ftw;../qml/compiler;../qml/memory;.", diff --git a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp index db9e621d54..816ac3f278 100644 --- a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp +++ b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp @@ -38,7 +38,8 @@ #include <QDebug> #include <QBuffer> -#include "../../../../../src/plugins/qmltooling/shared/qpacketprotocol.h" +#include <private/qpacketprotocol_p.h> +#include <private/qpacket_p.h> #include "debugutil_p.h" @@ -55,18 +56,10 @@ private slots: void init(); void cleanup(); - void maximumPacketSize(); - void setMaximumPacketSize(); - void setMaximumPacketSize_data(); void send(); - void send_data(); void packetsAvailable(); void packetsAvailable_data(); - void clear(); void read(); - void device(); - - void tst_QPacket_clear(); }; void tst_QPacketProtocol::init() @@ -95,48 +88,17 @@ void tst_QPacketProtocol::cleanup() delete m_server; } -void tst_QPacketProtocol::maximumPacketSize() -{ - QPacketProtocol p(m_client); - QCOMPARE(p.maximumPacketSize(), 0x7FFFFFFF); -} - -void tst_QPacketProtocol::setMaximumPacketSize() -{ - QFETCH(qint32, size); - QFETCH(qint32, expected); - - QPacketProtocol out(m_serverConn); - QCOMPARE(out.setMaximumPacketSize(size), expected); -} - -void tst_QPacketProtocol::setMaximumPacketSize_data() -{ - QTest::addColumn<int>("size"); - QTest::addColumn<int>("expected"); - - QTest::newRow("invalid") << qint32(sizeof(qint32) - 1) << qint32(0x7FFFFFFF); - QTest::newRow("still invalid") << qint32(sizeof(qint32)) << qint32(0x7FFFFFFF); - QTest::newRow("valid") << qint32(sizeof(qint32) + 1) << qint32(sizeof(qint32) + 1); -} - void tst_QPacketProtocol::send() { - QFETCH(bool, useAutoSend); - QPacketProtocol in(m_client); QPacketProtocol out(m_serverConn); QByteArray ba; int num; - if (useAutoSend) { - out.send() << "Hello world" << 123; - } else { - QPacket packet; - packet << "Hello world" << 123; - out.send(packet); - } + QPacket packet; + packet << "Hello world" << 123; + out.send(packet); QVERIFY(QQmlDebugTest::waitForSignal(&in, SIGNAL(readyRead()))); @@ -146,14 +108,6 @@ void tst_QPacketProtocol::send() QCOMPARE(num, 123); } -void tst_QPacketProtocol::send_data() -{ - QTest::addColumn<bool>("useAutoSend"); - - QTest::newRow("auto send") << true; - QTest::newRow("no auto send") << false; -} - void tst_QPacketProtocol::packetsAvailable() { QFETCH(int, packetCount); @@ -164,8 +118,11 @@ void tst_QPacketProtocol::packetsAvailable() QCOMPARE(out.packetsAvailable(), qint64(0)); QCOMPARE(in.packetsAvailable(), qint64(0)); - for (int i=0; i<packetCount; i++) - out.send() << "Hello"; + for (int i=0; i<packetCount; i++) { + QPacket packet; + packet << "Hello"; + out.send(packet); + } QVERIFY(QQmlDebugTest::waitForSignal(&in, SIGNAL(readyRead()))); QCOMPARE(in.packetsAvailable(), qint64(packetCount)); @@ -180,79 +137,35 @@ void tst_QPacketProtocol::packetsAvailable_data() QTest::newRow("10") << 10; } -void tst_QPacketProtocol::clear() -{ - QPacketProtocol in(m_client); - QPacketProtocol out(m_serverConn); - - out.send() << 123; - out.send() << 456; - QVERIFY(QQmlDebugTest::waitForSignal(&in, SIGNAL(readyRead()))); - - in.clear(); - QVERIFY(in.read().isEmpty()); -} - void tst_QPacketProtocol::read() { QPacketProtocol in(m_client); QPacketProtocol out(m_serverConn); - QVERIFY(in.read().isEmpty()); + QVERIFY(in.read().atEnd()); + + QPacket packet; + packet << 123; + out.send(packet); - out.send() << 123; - out.send() << 456; + QPacket packet2; + packet2 << 456; + out.send(packet2); QVERIFY(QQmlDebugTest::waitForSignal(&in, SIGNAL(readyRead()))); int num; QPacket p1 = in.read(); - QVERIFY(!p1.isEmpty()); + QVERIFY(!p1.atEnd()); p1 >> num; QCOMPARE(num, 123); QPacket p2 = in.read(); - QVERIFY(!p2.isEmpty()); + QVERIFY(!p2.atEnd()); p2 >> num; QCOMPARE(num, 456); - QVERIFY(in.read().isEmpty()); -} - -void tst_QPacketProtocol::device() -{ - QPacketProtocol p(m_client); - QCOMPARE(p.device(), m_client); -} - -void tst_QPacketProtocol::tst_QPacket_clear() -{ - QPacketProtocol protocol(m_client); - - QPacket packet; - - packet << "Hello world!" << 123; - protocol.send(packet); - - packet.clear(); - QVERIFY(packet.isEmpty()); - packet << "Goodbyte world!" << 789; - protocol.send(packet); - - QByteArray ba; - int num; - QPacketProtocol in(m_serverConn); - QVERIFY(QQmlDebugTest::waitForSignal(&in, SIGNAL(readyRead()))); - - QPacket p1 = in.read(); - p1 >> ba >> num; - QCOMPARE(ba, QByteArray("Hello world!") + '\0'); - QCOMPARE(num, 123); - - QPacket p2 = in.read(); - p2 >> ba >> num; - QCOMPARE(ba, QByteArray("Goodbyte world!") + '\0'); - QCOMPARE(num, 789); + QVERIFY(in.read().atEnd()); } QTEST_MAIN(tst_QPacketProtocol) diff --git a/tests/auto/qml/debugger/shared/debugutil.pri b/tests/auto/qml/debugger/shared/debugutil.pri index cb9c761395..73ed647061 100644 --- a/tests/auto/qml/debugger/shared/debugutil.pri +++ b/tests/auto/qml/debugger/shared/debugutil.pri @@ -1,8 +1,8 @@ +QT += packetprotocol-private + HEADERS += $$PWD/debugutil_p.h \ $$PWD/qqmldebugclient.h \ - $$PWD/../../../../../src/plugins/qmltooling/shared/qpacketprotocol.h SOURCES += $$PWD/debugutil.cpp \ $$PWD/qqmldebugclient.cpp \ - $$PWD/../../../../../src/plugins/qmltooling/shared/qpacketprotocol.cpp diff --git a/tests/auto/qml/debugger/shared/qqmldebugclient.cpp b/tests/auto/qml/debugger/shared/qqmldebugclient.cpp index f350614a47..00de8f9f7e 100644 --- a/tests/auto/qml/debugger/shared/qqmldebugclient.cpp +++ b/tests/auto/qml/debugger/shared/qqmldebugclient.cpp @@ -32,7 +32,9 @@ ****************************************************************************/ #include "qqmldebugclient.h" -#include "../../../../../src/plugins/qmltooling/shared/qpacketprotocol.h" + +#include <private/qpacketprotocol_p.h> +#include <private/qpacket_p.h> #include <QtCore/qdebug.h> #include <QtCore/qeventloop.h> @@ -138,7 +140,7 @@ void QQmlDebugConnectionPrivate::readyRead() QStringList pluginNames; QList<float> pluginVersions; pack >> pluginNames; - if (!pack.isEmpty()) + if (!pack.atEnd()) pack >> pluginVersions; const int pluginNamesSize = pluginNames.size(); @@ -192,7 +194,7 @@ void QQmlDebugConnectionPrivate::readyRead() QStringList pluginNames; QList<float> pluginVersions; pack >> pluginNames; - if (!pack.isEmpty()) + if (!pack.atEnd()) pack >> pluginVersions; const int pluginNamesSize = pluginNames.size(); diff --git a/tools/qmlprofiler/qmlprofiler.pro b/tools/qmlprofiler/qmlprofiler.pro index 4fa36f5127..e38ba928e2 100644 --- a/tools/qmlprofiler/qmlprofiler.pro +++ b/tools/qmlprofiler/qmlprofiler.pro @@ -1,4 +1,4 @@ -QT = qml qml-private network core-private +QT = qml qml-private network core-private packetprotocol-private CONFIG += no_import_scan SOURCES += main.cpp \ @@ -6,8 +6,7 @@ SOURCES += main.cpp \ commandlistener.cpp \ qqmldebugclient.cpp \ qmlprofilerdata.cpp \ - qmlprofilerclient.cpp \ - qpacketprotocol.cpp + qmlprofilerclient.cpp HEADERS += \ qmlprofilerapplication.h \ @@ -16,7 +15,6 @@ HEADERS += \ qmlprofilerdata.h \ qmlprofilerclient.h \ qmlprofilereventlocation.h \ - qqmldebugclient.h \ - qpacketprotocol.h + qqmldebugclient.h load(qt_tool) diff --git a/tools/qmlprofiler/qpacketprotocol.cpp b/tools/qmlprofiler/qpacketprotocol.cpp deleted file mode 100644 index 096bc142c5..0000000000 --- a/tools/qmlprofiler/qpacketprotocol.cpp +++ /dev/null @@ -1,527 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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. -** -** 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. -** -** 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. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qpacketprotocol.h" - -#include <QtCore/QBuffer> -#include <QtCore/QElapsedTimer> -#include <private/qiodevice_p.h> // for qt_subtract_from_timeout - -static const unsigned int MAX_PACKET_SIZE = 0x7FFFFFFF; - -/*! - \class QPacketProtocol - \internal - - \brief The QPacketProtocol class encapsulates communicating discrete packets - across fragmented IO channels, such as TCP sockets. - - QPacketProtocol makes it simple to send arbitrary sized data "packets" across - fragmented transports such as TCP and UDP. - - As transmission boundaries are not respected, sending packets over protocols - like TCP frequently involves "stitching" them back together at the receiver. - QPacketProtocol makes this easier by performing this task for you. Packet - data sent using QPacketProtocol is prepended with a 4-byte size header - allowing the receiving QPacketProtocol to buffer the packet internally until - it has all been received. QPacketProtocol does not perform any sanity - checking on the size or on the data, so this class should only be used in - prototyping or trusted situations where DOS attacks are unlikely. - - QPacketProtocol does not perform any communications itself. Instead it can - operate on any QIODevice that supports the QIODevice::readyRead() signal. A - logical "packet" is encapsulated by the companion QPacket class. The - following example shows two ways to send data using QPacketProtocol. The - transmitted data is equivalent in both. - - \code - QTcpSocket socket; - // ... connect socket ... - - QPacketProtocol protocol(&socket); - - // Send packet the quick way - protocol.send() << "Hello world" << 123; - - // Send packet the longer way - QPacket packet; - packet << "Hello world" << 123; - protocol.send(packet); - \endcode - - Likewise, the following shows how to read data from QPacketProtocol, assuming - that the QPacketProtocol::readyRead() signal has been emitted. - - \code - // ... QPacketProtocol::readyRead() is emitted ... - - int a; - QByteArray b; - - // Receive packet the quick way - protocol.read() >> a >> b; - - // Receive packet the longer way - QPacket packet = protocol.read(); - p >> a >> b; - \endcode - - \ingroup io - \sa QPacket -*/ - -class QPacketProtocolPrivate : public QObject -{ - Q_OBJECT -public: - QPacketProtocolPrivate(QPacketProtocol *parent, QIODevice *_dev) - : QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE), - waitingForPacket(false), dev(_dev) - { - Q_ASSERT(4 == sizeof(qint32)); - - QObject::connect(this, SIGNAL(readyRead()), - parent, SIGNAL(readyRead())); - QObject::connect(this, SIGNAL(packetWritten()), - parent, SIGNAL(packetWritten())); - QObject::connect(this, SIGNAL(invalidPacket()), - parent, SIGNAL(invalidPacket())); - QObject::connect(dev, SIGNAL(readyRead()), - this, SLOT(readyToRead())); - QObject::connect(dev, SIGNAL(aboutToClose()), - this, SLOT(aboutToClose())); - QObject::connect(dev, SIGNAL(bytesWritten(qint64)), - this, SLOT(bytesWritten(qint64))); - } - -Q_SIGNALS: - void readyRead(); - void packetWritten(); - void invalidPacket(); - -public Q_SLOTS: - void aboutToClose() - { - inProgress.clear(); - sendingPackets.clear(); - inProgressSize = -1; - } - - void bytesWritten(qint64 bytes) - { - Q_ASSERT(!sendingPackets.isEmpty()); - - while (bytes) { - if (sendingPackets.at(0) > bytes) { - sendingPackets[0] -= bytes; - bytes = 0; - } else { - bytes -= sendingPackets.at(0); - sendingPackets.removeFirst(); - emit packetWritten(); - } - } - } - - void readyToRead() - { - while (true) { - // Need to get trailing data - if (-1 == inProgressSize) { - // We need a size header of sizeof(qint32) - if (sizeof(qint32) > (uint)dev->bytesAvailable()) - return; - - // Read size header - int read = dev->read((char *)&inProgressSize, sizeof(qint32)); - Q_ASSERT(read == sizeof(qint32)); - Q_UNUSED(read); - - // Check sizing constraints - if (inProgressSize > maxPacketSize) { - QObject::disconnect(dev, SIGNAL(readyRead()), - this, SLOT(readyToRead())); - QObject::disconnect(dev, SIGNAL(aboutToClose()), - this, SLOT(aboutToClose())); - QObject::disconnect(dev, SIGNAL(bytesWritten(qint64)), - this, SLOT(bytesWritten(qint64))); - dev = 0; - emit invalidPacket(); - return; - } - - inProgressSize -= sizeof(qint32); - } else { - inProgress.append(dev->read(inProgressSize - inProgress.size())); - - if (inProgressSize == inProgress.size()) { - // Packet has arrived! - packets.append(inProgress); - inProgressSize = -1; - inProgress.clear(); - - waitingForPacket = false; - emit readyRead(); - } else - return; - } - } - } - -public: - QList<qint64> sendingPackets; - QList<QByteArray> packets; - QByteArray inProgress; - qint32 inProgressSize; - qint32 maxPacketSize; - bool waitingForPacket; - QIODevice *dev; -}; - -/*! - Construct a QPacketProtocol instance that works on \a dev with the - specified \a parent. - */ -QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent) - : QObject(parent), d(new QPacketProtocolPrivate(this, dev)) -{ - Q_ASSERT(dev); -} - -/*! - Destroys the QPacketProtocol instance. - */ -QPacketProtocol::~QPacketProtocol() -{ -} - -/*! - Returns the maximum packet size allowed. By default this is - 2,147,483,647 bytes. - - If a packet claiming to be larger than the maximum packet size is received, - the QPacketProtocol::invalidPacket() signal is emitted. - - \sa QPacketProtocol::setMaximumPacketSize() - */ -qint32 QPacketProtocol::maximumPacketSize() const -{ - return d->maxPacketSize; -} - -/*! - Sets the maximum allowable packet size to \a max. - - \sa QPacketProtocol::maximumPacketSize() - */ -qint32 QPacketProtocol::setMaximumPacketSize(qint32 max) -{ - if (max > (signed)sizeof(qint32)) - d->maxPacketSize = max; - return d->maxPacketSize; -} - -/*! - Returns a streamable object that is transmitted on destruction. For example - - \code - protocol.send() << "Hello world" << 123; - \endcode - - will send a packet containing "Hello world" and 123. To construct more - complex packets, explicitly construct a QPacket instance. - */ -QPacketAutoSend QPacketProtocol::send() -{ - return QPacketAutoSend(this); -} - -/*! - \fn void QPacketProtocol::send(const QPacket & packet) - - Transmit the \a packet. - */ -void QPacketProtocol::send(const QPacket & p) -{ - if (p.b.isEmpty()) - return; // We don't send empty packets - - qint64 sendSize = p.b.size() + sizeof(qint32); - - d->sendingPackets.append(sendSize); - qint32 sendSize32 = sendSize; - qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32)); - Q_UNUSED(writeBytes); - Q_ASSERT(writeBytes == sizeof(qint32)); - writeBytes = d->dev->write(p.b); - Q_ASSERT(writeBytes == p.b.size()); -} - -/*! - Returns the number of received packets yet to be read. - */ -qint64 QPacketProtocol::packetsAvailable() const -{ - return d->packets.count(); -} - -/*! - Discard any unread packets. - */ -void QPacketProtocol::clear() -{ - d->packets.clear(); -} - -/*! - Return the next unread packet, or an invalid QPacket instance if no packets - are available. This method does NOT block. - */ -QPacket QPacketProtocol::read() -{ - if (0 == d->packets.count()) - return QPacket(); - - QPacket rv(d->packets.at(0)); - d->packets.removeFirst(); - return rv; -} - -/*! - This function locks until a new packet is available for reading and the - \l{QIODevice::}{readyRead()} signal has been emitted. The function - will timeout after \a msecs milliseconds; the default timeout is - 30000 milliseconds. - - The function returns true if the readyRead() signal is emitted and - there is new data available for reading; otherwise it returns false - (if an error occurred or the operation timed out). - */ - -bool QPacketProtocol::waitForReadyRead(int msecs) -{ - if (!d->packets.isEmpty()) - return true; - - QElapsedTimer stopWatch; - stopWatch.start(); - - d->waitingForPacket = true; - do { - if (!d->dev->waitForReadyRead(msecs)) - return false; - if (!d->waitingForPacket) - return true; - msecs = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); - } while (true); -} - -/*! - Return the QIODevice passed to the QPacketProtocol constructor. -*/ -QIODevice *QPacketProtocol::device() -{ - return d->dev; -} - -/*! - \fn void QPacketProtocol::readyRead() - - Emitted whenever a new packet is received. Applications may use - QPacketProtocol::read() to retrieve this packet. - */ - -/*! - \fn void QPacketProtocol::invalidPacket() - - A packet larger than the maximum allowable packet size was received. The - packet will be discarded and, as it indicates corruption in the protocol, no - further packets will be received. - */ - -/*! - \fn void QPacketProtocol::packetWritten() - - Emitted each time a packet is completing written to the device. This signal - may be used for communications flow control. - */ - -/*! - \class QPacket - \internal - - \brief The QPacket class encapsulates an unfragmentable packet of data to be - transmitted by QPacketProtocol. - - The QPacket class works together with QPacketProtocol to make it simple to - send arbitrary sized data "packets" across fragmented transports such as TCP - and UDP. - - QPacket provides a QDataStream interface to an unfragmentable packet. - Applications should construct a QPacket, propagate it with data and then - transmit it over a QPacketProtocol instance. For example: - \code - QPacketProtocol protocol(...); - - QPacket myPacket; - myPacket << "Hello world!" << 123; - protocol.send(myPacket); - \endcode - - As long as both ends of the connection are using the QPacketProtocol class, - the data within this packet will be delivered unfragmented at the other end, - ready for extraction. - - \code - QByteArray greeting; - int count; - - QPacket myPacket = protocol.read(); - - myPacket >> greeting >> count; - \endcode - - Only packets returned from QPacketProtocol::read() may be read from. QPacket - instances constructed by directly by applications are for transmission only - and are considered "write only". Attempting to read data from them will - result in undefined behavior. - - \ingroup io - \sa QPacketProtocol - */ - -/*! - Constructs an empty write-only packet. - */ -QPacket::QPacket() - : QDataStream(), buf(0) -{ - buf = new QBuffer(&b); - buf->open(QIODevice::WriteOnly); - setDevice(buf); - setVersion(QDataStream::Qt_4_7); -} - -/*! - Destroys the QPacket instance. - */ -QPacket::~QPacket() -{ - if (buf) { - delete buf; - buf = 0; - } -} - -/*! - Creates a copy of \a other. The initial stream positions are shared, but the - two packets are otherwise independent. - */ -QPacket::QPacket(const QPacket & other) - : QDataStream(), b(other.b), buf(0) -{ - buf = new QBuffer(&b); - buf->open(other.buf->openMode()); - setDevice(buf); -} - -/*! - \internal - */ -QPacket::QPacket(const QByteArray & ba) - : QDataStream(), b(ba), buf(0) -{ - buf = new QBuffer(&b); - buf->open(QIODevice::ReadOnly); - setDevice(buf); -} - -/*! - Returns true if this packet is empty - that is, contains no data. - */ -bool QPacket::isEmpty() const -{ - return b.isEmpty(); -} - -/*! - Returns raw packet data. - */ -QByteArray QPacket::data() const -{ - return b; -} - -/*! - Clears data in the packet. This is useful for reusing one writable packet. - For example - \code - QPacketProtocol protocol(...); - - QPacket packet; - - packet << "Hello world!" << 123; - protocol.send(packet); - - packet.clear(); - packet << "Goodbyte world!" << 789; - protocol.send(packet); - \endcode - */ -void QPacket::clear() -{ - QBuffer::OpenMode oldMode = buf->openMode(); - buf->close(); - b.clear(); - buf->setBuffer(&b); // reset QBuffer internals with new size of b. - buf->open(oldMode); -} - -/*! - \class QPacketAutoSend - \internal - - \internal - */ -QPacketAutoSend::QPacketAutoSend(QPacketProtocol *_p) - : QPacket(), p(_p) -{ -} - -QPacketAutoSend::~QPacketAutoSend() -{ - if (!b.isEmpty()) - p->send(*this); -} - -#include <qpacketprotocol.moc> diff --git a/tools/qmlprofiler/qqmldebugclient.cpp b/tools/qmlprofiler/qqmldebugclient.cpp index f87d4b0a7c..d661c71e1c 100644 --- a/tools/qmlprofiler/qqmldebugclient.cpp +++ b/tools/qmlprofiler/qqmldebugclient.cpp @@ -32,7 +32,9 @@ ****************************************************************************/ #include "qqmldebugclient.h" -#include "qpacketprotocol.h" + +#include <private/qpacketprotocol_p.h> +#include <private/qpacket_p.h> #include <QtCore/qdebug.h> #include <QtCore/qstringlist.h> @@ -119,7 +121,7 @@ void QQmlDebugConnectionPrivate::readyRead() QStringList pluginNames; QList<float> pluginVersions; pack >> pluginNames; - if (!pack.isEmpty()) + if (!pack.atEnd()) pack >> pluginVersions; const int pluginNamesSize = pluginNames.size(); @@ -169,7 +171,7 @@ void QQmlDebugConnectionPrivate::readyRead() QStringList pluginNames; QList<float> pluginVersions; pack >> pluginNames; - if (!pack.isEmpty()) + if (!pack.atEnd()) pack >> pluginVersions; const int pluginNamesSize = pluginNames.size(); |