1 /*
2   message.h
3 
4   This file is part of GammaRay, the Qt application inspection and
5   manipulation tool.
6 
7   Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
8   Author: Volker Krause <volker.krause@kdab.com>
9 
10   Licensees holding valid commercial KDAB GammaRay licenses may use this file in
11   accordance with GammaRay Commercial License Agreement provided with the Software.
12 
13   Contact info@kdab.com if any conditions of this licensing are not clear to you.
14 
15   This program is free software; you can redistribute it and/or modify
16   it under the terms of the GNU General Public License as published by
17   the Free Software Foundation, either version 2 of the License, or
18   (at your option) any later version.
19 
20   This program is distributed in the hope that it will be useful,
21   but WITHOUT ANY WARRANTY; without even the implied warranty of
22   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23   GNU General Public License for more details.
24 
25   You should have received a copy of the GNU General Public License
26   along with this program.  If not, see <http://www.gnu.org/licenses/>.
27 */
28 
29 #ifndef GAMMARAY_MESSAGE_H
30 #define GAMMARAY_MESSAGE_H
31 
32 #include "gammaray_common_export.h"
33 #include "protocol.h"
34 
35 #include <QByteArray>
36 #include <QDataStream>
37 
38 #include <functional>
39 #include <memory>
40 
41 class MessageBuffer;
42 
43 namespace GammaRay {
44 /**
45  * Single message send between client and server.
46  * Binary format:
47  * - sizeof(Protocol::PayloadSize) byte size of the message payload (not including the size and other fixed fields itself) in netowork byte order (big endian)
48  * - sizeof(Protocol::ObjectAddress) server object address (big endian)
49  * - sizeof(Protocol::MessageType) command type (big endian)
50  * - size bytes message payload (encoding is user defined, QDataStream provided for convenience)
51  */
52 class GAMMARAY_COMMON_EXPORT Message
53 {
54 public:
55     /**
56      * Construct a new message to/from @p objectAddress and message type @p type.
57      */
58     explicit Message(Protocol::ObjectAddress objectAddress, Protocol::MessageType type);
59     Message(Message &&other) Q_DECL_NOEXCEPT; // krazy:exclude=explicit
60     ~Message();
61 
62     Protocol::ObjectAddress address() const;
63     Protocol::MessageType type() const;
64 
65     /** Read value from the payload
66      *  This operator proxy over payload() allow to do:
67      *   - Run time check on the stream status
68      */
69     template <typename T>
70     GammaRay::Message &operator>>(T &value)
71     {
72         if (Q_UNLIKELY(payload().status() != QDataStream::Ok)) {
73             qWarning("%s: Attempting to read from a non valid stream: status: %i", Q_FUNC_INFO, int(payload().status()));
74         }
75         payload() >> value;
76         if (Q_UNLIKELY(payload().status() != QDataStream::Ok)) {
77             qWarning("%s: Read from a non valid stream: status: %i", Q_FUNC_INFO, int(payload().status()));
78         }
79         return *this;
80     }
81 
82     /** Read value from the payload
83      *  This overload allow to read content from a const Message.
84      */
85     template <typename T>
86     GammaRay::Message &operator>>(T &value) const
87     {
88         return const_cast<GammaRay::Message *>(this)->operator>>(value);
89     }
90 
91     /** Write value to the payload.
92      *  This operator proxy over payload() allow to do:
93      *   - Run time check on the stream status
94      */
95     template <typename T>
96     GammaRay::Message &operator<<(const T &value)
97     {
98         if (Q_UNLIKELY(payload().status() != QDataStream::Ok)) {
99             qWarning("%s: Attempting to write to a non valid stream: status: %i", Q_FUNC_INFO, int(payload().status()));
100         }
101         payload() << value;
102         if (Q_UNLIKELY(payload().status() != QDataStream::Ok)) {
103             qWarning("%s: Write to a non valid stream: status: %i", Q_FUNC_INFO, int(payload().status()));
104         }
105         return *this;
106     }
107 
108     /** Checks if there is a full message waiting in @p device. */
109     static bool canReadMessage(QIODevice *device);
110     /** Read the next message from @p device. */
111     static Message readMessage(QIODevice *device);
112 
113     static quint8 lowestSupportedDataVersion();
114     static quint8 highestSupportedDataVersion();
115 
116     static quint8 negotiatedDataVersion();
117     static void setNegotiatedDataVersion(quint8 version);
118     static void resetNegotiatedDataVersion();
119 
120     /** Write this message to @p device. */
121     void write(QIODevice *device) const;
122 
123     /** Size of the uncompressed message payload. */
124     int size() const;
125 
126 private:
127     Message();
128 
129     /** Access to the message payload. This is read-only for received messages
130      *  and write-only for messages to be sent.
131      */
132     QDataStream &payload() const;
133 
134     Protocol::ObjectAddress m_objectAddress;
135     Protocol::MessageType m_messageType;
136 
137     std::unique_ptr<MessageBuffer, std::function<void(MessageBuffer *)>> m_buffer;
138 };
139 }
140 
141 #endif
142