File indexing completed on 2024-09-15 04:36:24
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 #pragma once 0009 0010 #include <chrono> 0011 #include <type_traits> 0012 0013 #include "akonadiprivate_export.h" 0014 #include "protocol_exception_p.h" 0015 0016 #include <QByteArray> 0017 #include <QIODevice> 0018 #include <QTimeZone> 0019 0020 namespace Akonadi::Protocol 0021 { 0022 0023 class AKONADIPRIVATE_EXPORT DataStream 0024 { 0025 public: 0026 explicit DataStream(); 0027 explicit DataStream(QIODevice *device); 0028 ~DataStream(); 0029 0030 static void waitForData(QIODevice *device, int timeoutMs); 0031 0032 QIODevice *device() const; 0033 void setDevice(QIODevice *device); 0034 0035 std::chrono::milliseconds waitTimeout() const; 0036 void setWaitTimeout(std::chrono::milliseconds timeout); 0037 0038 void flush(); 0039 0040 template<typename T> 0041 requires(std::is_integral_v<T>) 0042 inline DataStream &operator<<(T val); 0043 template<typename T> 0044 requires(std::is_enum_v<T>) 0045 inline DataStream &operator<<(T val); 0046 0047 inline DataStream &operator<<(const QString &str); 0048 inline DataStream &operator<<(const QByteArray &data); 0049 inline DataStream &operator<<(const QDateTime &dt); 0050 0051 template<typename T> 0052 requires(std::is_integral_v<T>) 0053 inline DataStream &operator>>(T &val); 0054 template<typename T> 0055 requires(std::is_enum_v<T>) 0056 inline DataStream &operator>>(T &val); 0057 inline DataStream &operator>>(QString &str); 0058 inline DataStream &operator>>(QByteArray &data); 0059 inline DataStream &operator>>(QDateTime &dt); 0060 0061 void writeRawData(const char *data, qsizetype len); 0062 void writeBytes(const char *bytes, qsizetype len); 0063 0064 [[nodiscard]] qint64 readRawData(char *buffer, qint64 len); 0065 0066 void waitForData(quint32 size); 0067 0068 private: 0069 Q_DISABLE_COPY(DataStream) 0070 0071 inline void checkDevice() const 0072 { 0073 if (Q_UNLIKELY(!mDev)) { 0074 throw ProtocolException("Device does not exist"); 0075 } 0076 } 0077 0078 QIODevice *mDev; 0079 QByteArray mWriteBuffer; 0080 std::chrono::milliseconds mWaitTimeout = std::chrono::seconds{30}; 0081 }; 0082 0083 template<typename T> 0084 requires(std::is_integral_v<T>) 0085 inline DataStream &DataStream::operator<<(T val) 0086 { 0087 checkDevice(); 0088 writeRawData((char *)&val, sizeof(T)); 0089 return *this; 0090 } 0091 0092 template<typename T> 0093 requires(std::is_enum_v<T>) 0094 inline DataStream &DataStream::operator<<(T val) 0095 { 0096 return *this << (typename std::underlying_type<T>::type)val; 0097 } 0098 0099 inline DataStream &DataStream::operator<<(const QString &str) 0100 { 0101 if (str.isNull()) { 0102 *this << (quint32)0xffffffff; 0103 } else { 0104 writeBytes(reinterpret_cast<const char *>(str.unicode()), sizeof(QChar) * str.length()); 0105 } 0106 return *this; 0107 } 0108 0109 inline DataStream &DataStream::operator<<(const QByteArray &data) 0110 { 0111 if (data.isNull()) { 0112 *this << (quint32)0xffffffff; 0113 } else { 0114 writeBytes(data.constData(), data.size()); 0115 } 0116 return *this; 0117 } 0118 0119 inline DataStream &DataStream::operator<<(const QDateTime &dt) 0120 { 0121 *this << dt.date().toJulianDay() << dt.time().msecsSinceStartOfDay() << dt.timeSpec(); 0122 if (dt.timeSpec() == Qt::OffsetFromUTC) { 0123 *this << dt.offsetFromUtc(); 0124 } else if (dt.timeSpec() == Qt::TimeZone) { 0125 *this << dt.timeZone().id(); 0126 } 0127 return *this; 0128 } 0129 0130 template<typename T> 0131 requires(std::is_integral_v<T>) 0132 inline DataStream &DataStream::operator>>(T &val) 0133 { 0134 checkDevice(); 0135 0136 waitForData(sizeof(T)); 0137 0138 if (mDev->read((char *)&val, sizeof(T)) != sizeof(T)) { 0139 throw Akonadi::ProtocolException("Failed to read enough data from stream"); 0140 } 0141 return *this; 0142 } 0143 0144 template<typename T> 0145 requires(std::is_enum_v<T>) 0146 inline DataStream &DataStream::operator>>(T &val) 0147 { 0148 return *this >> reinterpret_cast<typename std::underlying_type<T>::type &>(val); 0149 } 0150 0151 inline DataStream &DataStream::operator>>(QString &str) 0152 { 0153 str.clear(); 0154 0155 quint32 bytes = 0; 0156 *this >> bytes; 0157 if (bytes == 0xffffffff) { 0158 return *this; 0159 } else if (bytes == 0) { 0160 str = QString(QLatin1StringView("")); 0161 return *this; 0162 } 0163 0164 if (bytes & 0x1) { 0165 str.clear(); 0166 throw Akonadi::ProtocolException("Read corrupt data"); 0167 } 0168 0169 const quint32 step = 1024 * 1024; 0170 const quint32 len = bytes / 2; 0171 quint32 allocated = 0; 0172 0173 while (allocated < len) { 0174 const int blockSize = qMin(step, len - allocated); 0175 waitForData(blockSize * sizeof(QChar)); 0176 str.resize(allocated + blockSize); 0177 if (readRawData(reinterpret_cast<char *>(str.data()) + allocated * sizeof(QChar), blockSize * sizeof(QChar)) != int(blockSize * sizeof(QChar))) { 0178 throw Akonadi::ProtocolException("Failed to read enough data from stream"); 0179 } 0180 allocated += blockSize; 0181 } 0182 0183 return *this; 0184 } 0185 0186 inline DataStream &DataStream::operator>>(QByteArray &data) 0187 { 0188 data.clear(); 0189 0190 quint32 len = 0; 0191 *this >> len; 0192 if (len == 0xffffffff) { 0193 return *this; 0194 } 0195 0196 const quint32 step = 1024 * 1024; 0197 quint32 allocated = 0; 0198 0199 while (allocated < len) { 0200 const int blockSize = qMin(step, len - allocated); 0201 waitForData(blockSize); 0202 data.resize(allocated + blockSize); 0203 if (readRawData(data.data() + allocated, blockSize) != blockSize) { 0204 throw Akonadi::ProtocolException("Failed to read enough data from stream"); 0205 } 0206 allocated += blockSize; 0207 } 0208 0209 return *this; 0210 } 0211 0212 inline DataStream &DataStream::operator>>(QDateTime &dt) 0213 { 0214 QDate date; 0215 QTime time; 0216 qint64 jd; 0217 int mds; 0218 Qt::TimeSpec spec; 0219 0220 *this >> jd >> mds >> spec; 0221 date = QDate::fromJulianDay(jd); 0222 time = QTime::fromMSecsSinceStartOfDay(mds); 0223 if (spec == Qt::OffsetFromUTC) { 0224 int offset = 0; 0225 *this >> offset; 0226 dt = QDateTime(date, time, spec, offset); 0227 } else if (spec == Qt::TimeZone) { 0228 QByteArray id; 0229 *this >> id; 0230 dt = QDateTime(date, time, QTimeZone(id)); 0231 } else { 0232 dt = QDateTime(date, time, spec); 0233 } 0234 0235 return *this; 0236 } 0237 0238 } // namespace Akonadi::Protocol 0239 0240 // Inline functions 0241 0242 template<typename T> 0243 inline Akonadi::Protocol::DataStream &operator<<(Akonadi::Protocol::DataStream &stream, QFlags<T> flags) 0244 { 0245 return stream << static_cast<typename QFlags<T>::Int>(flags); 0246 } 0247 0248 template<typename T> 0249 inline Akonadi::Protocol::DataStream &operator>>(Akonadi::Protocol::DataStream &stream, QFlags<T> &flags) 0250 { 0251 stream >> reinterpret_cast<typename QFlags<T>::Int &>(flags); 0252 return stream; 0253 } 0254 0255 // Generic streaming for all Qt value-based containers (as well as std containers that 0256 // implement operator<< for appending) 0257 template<typename T, template<typename> class Container> 0258 // typename std::enable_if<is_compatible_value_container<Container>::value, Akonadi::Protocol::DataStream>::type 0259 inline Akonadi::Protocol::DataStream &operator<<(Akonadi::Protocol::DataStream &stream, const Container<T> &list) 0260 { 0261 stream << (quint32)list.size(); 0262 for (auto iter = list.cbegin(), end = list.cend(); iter != end; ++iter) { 0263 stream << *iter; 0264 } 0265 return stream; 0266 } 0267 0268 template<typename T, template<typename> class Container> 0269 // //typename std::enable_if<is_compatible_container<Container>::value, Akonadi::Protocol::DataStream>::type 0270 inline Akonadi::Protocol::DataStream &operator>>(Akonadi::Protocol::DataStream &stream, Container<T> &list) 0271 { 0272 list.clear(); 0273 quint32 size = 0; 0274 stream >> size; 0275 list.reserve(size); 0276 for (quint32 i = 0; i < size; ++i) { 0277 T t; 0278 stream >> t; 0279 list << t; 0280 } 0281 return stream; 0282 } 0283 0284 namespace Akonadi::Protocol::Private 0285 { 0286 0287 template<typename Key, typename Value, template<typename, typename> class Container> 0288 inline void container_reserve(Container<Key, Value> &container, int size) 0289 { 0290 container.reserve(size); 0291 } 0292 0293 template<typename Key, typename Value> 0294 inline void container_reserve(QMap<Key, Value> &, int) 0295 { 0296 // noop 0297 } 0298 0299 } // namespace Akonadi::Protocol::Private 0300 0301 // Generic streaming for all Qt dictionary-based containers 0302 template<typename Key, typename Value, template<typename, typename> class Container> 0303 // typename std::enable_if<is_compatible_dictionary_container<Container>::value, Akonadi::Protocol::DataStream>::type 0304 inline Akonadi::Protocol::DataStream &operator<<(Akonadi::Protocol::DataStream &stream, const Container<Key, Value> &map) 0305 { 0306 stream << (quint32)map.size(); 0307 auto iter = map.cend(), begin = map.cbegin(); 0308 while (iter != begin) { 0309 --iter; 0310 stream << iter.key() << iter.value(); 0311 } 0312 return stream; 0313 } 0314 0315 template<typename Key, typename Value, template<typename, typename> class Container> 0316 // typename std::enable_if<is_compatible_dictionary_container<Container>::value, Akonadi::Protocol::DataStream>::type 0317 inline Akonadi::Protocol::DataStream &operator>>(Akonadi::Protocol::DataStream &stream, Container<Key, Value> &map) 0318 { 0319 map.clear(); 0320 quint32 size = 0; 0321 stream >> size; 0322 Akonadi::Protocol::Private::container_reserve(map, size); 0323 for (quint32 i = 0; i < size; ++i) { 0324 Key key; 0325 Value value; 0326 stream >> key >> value; 0327 map.insert(key, value); 0328 } 0329 return stream; 0330 }