File indexing completed on 2024-11-10 04:40:47
0001 /* 0002 * SPDX-FileCopyrightText: 2015 Daniel Vrátil <dvratil@redhat.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-or-later 0005 * 0006 */ 0007 0008 #include "datastream_p_p.h" 0009 0010 #ifdef Q_OS_WIN 0011 #include <QEventLoop> 0012 #include <QLocalSocket> 0013 #include <QTimer> 0014 #endif 0015 0016 using namespace Akonadi; 0017 using namespace Akonadi::Protocol; 0018 0019 DataStream::DataStream() 0020 : mDev(nullptr) 0021 { 0022 } 0023 0024 DataStream::DataStream(QIODevice *device) 0025 : mDev(device) 0026 { 0027 } 0028 0029 DataStream::~DataStream() 0030 { 0031 // No flush() here. Throwing an exception in a destructor would go badly. The caller MUST call flush after writing. 0032 } 0033 0034 void DataStream::flush() 0035 { 0036 if (!mWriteBuffer.isEmpty()) { 0037 const int len = mWriteBuffer.size(); 0038 int ret = mDev->write(mWriteBuffer); 0039 if (ret != len) { 0040 // TODO: Try to write data again unless ret is -1? 0041 throw ProtocolException("Failed to write all data"); 0042 } 0043 mWriteBuffer.clear(); 0044 } 0045 } 0046 0047 void DataStream::waitForData(QIODevice *device, int timeoutMs) 0048 { 0049 #ifdef Q_OS_WIN 0050 // Apparently readyRead() gets emitted sometimes even if there are no data 0051 // so we will re-enter the wait again immediately 0052 while (device->bytesAvailable() == 0) { 0053 auto ls = qobject_cast<QLocalSocket *>(device); 0054 if (ls && ls->state() != QLocalSocket::ConnectedState) { 0055 throw ProtocolException("Socket not connected to server"); 0056 } 0057 0058 QEventLoop loop; 0059 QObject::connect(device, &QIODevice::readyRead, &loop, &QEventLoop::quit); 0060 if (ls) { 0061 QObject::connect(ls, &QLocalSocket::stateChanged, &loop, &QEventLoop::quit); 0062 } 0063 bool timeout = false; 0064 if (timeoutMs > 0) { 0065 QTimer::singleShot(timeoutMs, &loop, [&]() { 0066 timeout = true; 0067 loop.quit(); 0068 }); 0069 } 0070 loop.exec(); 0071 if (timeout) { 0072 throw ProtocolException("Timeout while waiting for data"); 0073 } 0074 if (ls && ls->state() != QLocalSocket::ConnectedState) { 0075 throw ProtocolException("Socket not connected to server"); 0076 } 0077 } 0078 #else 0079 if (!device->waitForReadyRead(timeoutMs)) { 0080 throw ProtocolException("Timeout while waiting for data"); 0081 } 0082 #endif 0083 } 0084 0085 QIODevice *DataStream::device() const 0086 { 0087 return mDev; 0088 } 0089 0090 void DataStream::setDevice(QIODevice *device) 0091 { 0092 mDev = device; 0093 } 0094 0095 std::chrono::milliseconds DataStream::waitTimeout() const 0096 { 0097 return mWaitTimeout; 0098 } 0099 void DataStream::setWaitTimeout(std::chrono::milliseconds timeout) 0100 { 0101 mWaitTimeout = timeout; 0102 } 0103 0104 void DataStream::waitForData(quint32 size) 0105 { 0106 checkDevice(); 0107 0108 while (mDev->bytesAvailable() < size) { 0109 waitForData(mDev, mWaitTimeout.count()); 0110 } 0111 } 0112 0113 void DataStream::writeRawData(const char *data, qsizetype len) 0114 { 0115 checkDevice(); 0116 0117 mWriteBuffer += QByteArray::fromRawData(data, len); 0118 } 0119 0120 void DataStream::writeBytes(const char *bytes, qsizetype len) 0121 { 0122 *this << static_cast<quint32>(len); 0123 if (len) { 0124 writeRawData(bytes, len); 0125 } 0126 } 0127 0128 qint64 DataStream::readRawData(char *buffer, qint64 len) 0129 { 0130 checkDevice(); 0131 0132 return mDev->read(buffer, len); 0133 }