Line data Source code
1 : #ifndef __NETWORK_MESSAGE_HPP__
2 : #define __NETWORK_MESSAGE_HPP__
3 :
4 : #include "communication/NetworkEvent.hpp"
5 :
6 : #include <span>
7 : #include <vector>
8 : #include <string>
9 : #include <tuple>
10 : #include <expected>
11 : #include <cmath>
12 : #include <utility>
13 :
14 : namespace Soldank
15 : {
16 : enum class ParseError : unsigned short
17 : {
18 : InvalidStringSize = 0,
19 : BufferTooSmall,
20 : BufferTooBig,
21 : InvalidString,
22 : InvalidNetworkEvent,
23 : };
24 :
25 : namespace
26 : {
27 : template<unsigned int N>
28 : struct NetworkMessageData
29 : {
30 : template<typename Arg>
31 : static typename std::enable_if<!std::is_same<std::string, Arg>::value,
32 : std::expected<Arg, ParseError>>::type
33 61 : ParseDataParameter(std::span<const char> data)
34 : {
35 61 : if (data.size() < sizeof(Arg)) {
36 0 : return std::unexpected(ParseError::BufferTooSmall);
37 : }
38 :
39 61 : Arg converted_data =
40 61 : *static_cast<const Arg*>(static_cast<const void*>(data.subspan(0, sizeof(Arg)).data()));
41 61 : return converted_data;
42 : }
43 :
44 : template<typename Arg>
45 : static typename std::enable_if<std::is_same<std::string, Arg>::value,
46 : std::expected<std::string, ParseError>>::type
47 6 : ParseDataParameter(std::span<const char> data)
48 : {
49 : auto text_size_or_error =
50 6 : ParseDataParameter<unsigned short>(data.subspan(0, sizeof(unsigned short)));
51 6 : if (!text_size_or_error.has_value()) {
52 0 : return std::unexpected(text_size_or_error.error());
53 : }
54 :
55 6 : unsigned short text_size = *text_size_or_error;
56 6 : if (text_size == 0 || text_size > data.size() - 2) {
57 0 : return std::unexpected(ParseError::InvalidStringSize);
58 : }
59 :
60 60 : for (char character : data.subspan(2, text_size)) {
61 55 : if (character == 0) {
62 1 : return std::unexpected(ParseError::InvalidString);
63 : }
64 : }
65 :
66 5 : auto text_data = data.subspan(2, text_size);
67 10 : std::string text{ text_data.begin(), text_data.end() };
68 5 : return text;
69 5 : }
70 :
71 : template<typename Arg>
72 : static typename std::enable_if<!std::is_same<std::string, Arg>::value,
73 : std::expected<unsigned int, ParseError>>::type
74 49 : ParseDataParameterSize(std::span<const char> data)
75 : {
76 49 : if (data.size() < sizeof(Arg)) {
77 4 : return std::unexpected(ParseError::BufferTooSmall);
78 : }
79 :
80 45 : return sizeof(Arg);
81 : }
82 :
83 : template<typename Arg>
84 : static typename std::enable_if<std::is_same<std::string, Arg>::value,
85 : std::expected<unsigned int, ParseError>>::type
86 11 : ParseDataParameterSize(std::span<const char> data)
87 : {
88 : auto text_size_or_error =
89 11 : ParseDataParameter<unsigned short>(data.subspan(0, sizeof(unsigned short)));
90 11 : if (!text_size_or_error.has_value()) {
91 0 : return std::unexpected(text_size_or_error.error());
92 : }
93 11 : unsigned short text_size = *text_size_or_error;
94 11 : if ((text_size == 0) || text_size > data.size() - 2) {
95 3 : return std::unexpected(ParseError::InvalidStringSize);
96 : }
97 :
98 8 : return sizeof(unsigned short) + text_size;
99 : }
100 :
101 : template<typename Head, typename... Tail>
102 : static
103 : typename std::enable_if<N != 1, std::expected<std::tuple<Head, Tail...>, ParseError>>::type
104 28 : ParseData(std::span<const char> data)
105 : {
106 28 : if (data.empty()) {
107 2 : return std::unexpected(ParseError::BufferTooSmall);
108 : }
109 :
110 26 : auto head_or_error = ParseDataParameter<Head>(data);
111 26 : if (!head_or_error.has_value()) {
112 0 : return std::unexpected(head_or_error.error());
113 : }
114 :
115 26 : auto tail_offset_or_error = ParseDataParameterSize<Head>(data);
116 26 : if (!tail_offset_or_error.has_value()) {
117 0 : return std::unexpected(tail_offset_or_error.error());
118 : }
119 :
120 26 : auto tail_or_error = NetworkMessageData<N - 1>::template ParseData<Tail...>(
121 26 : data.subspan(*tail_offset_or_error));
122 26 : if (!tail_or_error.has_value()) {
123 13 : return std::unexpected(tail_or_error.error());
124 : }
125 13 : std::tuple<Head> head_in_tuple{ *head_or_error };
126 13 : return std::tuple_cat(head_in_tuple, *tail_or_error);
127 17 : }
128 :
129 : template<typename Arg>
130 : static typename std::enable_if<N == 1, std::expected<std::tuple<Arg>, ParseError>>::type
131 36 : ParseData(std::span<const char> data)
132 : {
133 36 : if (data.empty()) {
134 2 : return std::unexpected(ParseError::BufferTooSmall);
135 : }
136 :
137 34 : auto last_parameter_size_or_error = ParseDataParameterSize<Arg>(data);
138 34 : if (!last_parameter_size_or_error.has_value()) {
139 7 : return std::unexpected(last_parameter_size_or_error.error());
140 : }
141 27 : auto last_parameter_size = *last_parameter_size_or_error;
142 27 : if (data.size() > last_parameter_size) {
143 3 : return std::unexpected(ParseError::BufferTooBig);
144 : }
145 :
146 24 : auto parsed_or_error = ParseDataParameter<Arg>(data);
147 24 : if (!parsed_or_error.has_value()) {
148 1 : return std::unexpected(parsed_or_error.error());
149 : }
150 :
151 23 : return { *parsed_or_error };
152 5 : }
153 : };
154 : } // namespace
155 :
156 : class NetworkMessage
157 : {
158 : public:
159 9 : NetworkMessage(std::span<const char> data)
160 18 : : data_{ data.begin(), data.end() }
161 : {
162 9 : }
163 :
164 2 : NetworkMessage(NetworkEvent event)
165 2 : {
166 2 : auto event_value = std::to_underlying(event);
167 2 : AppendBytes(event_value);
168 2 : }
169 :
170 : template<class Arg>
171 : NetworkMessage(NetworkEvent event, Arg one_arg, unsigned int size)
172 : {
173 : auto event_value = std::to_underlying(event);
174 : AppendBytes(event_value);
175 : AppendBytes(one_arg, size);
176 : }
177 :
178 : template<class... Args>
179 9 : NetworkMessage(NetworkEvent event, Args... args)
180 9 : {
181 9 : auto event_value = std::to_underlying(event);
182 9 : AppendBytes(event_value);
183 9 : AppendBytes(args...);
184 9 : }
185 :
186 : template<class Head, class... Tail>
187 6 : void AppendBytes(Head head, Tail... tail)
188 : {
189 6 : AppendBytes(head);
190 6 : AppendBytes(tail...);
191 6 : }
192 :
193 : void AppendBytes(const std::string& text)
194 : {
195 : unsigned short text_length = text.length();
196 : auto text_length_bytes_to_append =
197 : std::span{ static_cast<const char*>(static_cast<void*>(&text_length)),
198 : sizeof(text_length) };
199 : data_.insert(
200 : data_.end(), text_length_bytes_to_append.cbegin(), text_length_bytes_to_append.cend());
201 : auto text_bytes_to_append =
202 : std::span{ static_cast<const char*>(static_cast<const void*>(text.c_str())),
203 : text_length };
204 : data_.insert(data_.end(), text_bytes_to_append.cbegin(), text_bytes_to_append.cend());
205 : }
206 :
207 7 : void AppendBytes(const char* head)
208 : {
209 7 : std::string text = head;
210 7 : unsigned short text_length = text.length();
211 : auto text_length_bytes_to_append =
212 : std::span{ static_cast<const char*>(static_cast<void*>(&text_length)),
213 7 : sizeof(text_length) };
214 14 : data_.insert(
215 7 : data_.end(), text_length_bytes_to_append.cbegin(), text_length_bytes_to_append.cend());
216 : auto text_bytes_to_append =
217 : std::span{ static_cast<const char*>(static_cast<const void*>(text.c_str())),
218 7 : text_length };
219 7 : data_.insert(data_.end(), text_bytes_to_append.cbegin(), text_bytes_to_append.cend());
220 7 : }
221 :
222 : template<typename Head>
223 19 : void AppendBytes(Head head)
224 : {
225 19 : auto head_bytes_to_append =
226 : std::span{ static_cast<const char*>(static_cast<void*>(&head)), sizeof(Head) };
227 19 : data_.insert(data_.end(), head_bytes_to_append.cbegin(), head_bytes_to_append.cend());
228 19 : }
229 :
230 12 : std::span<const char> GetData() const { return { data_ }; }
231 :
232 : template<typename... Args>
233 15 : static std::expected<std::tuple<Args...>, ParseError> ParseData(std::span<const char> data)
234 : {
235 15 : return NetworkMessageData<sizeof...(Args)>::template ParseData<Args...>(data);
236 : }
237 :
238 : template<typename... Args>
239 10 : std::expected<std::tuple<Args...>, ParseError> Parse() const
240 : {
241 10 : return NetworkMessageData<sizeof...(Args)>::template ParseData<Args...>(data_);
242 : }
243 :
244 14 : std::expected<NetworkEvent, ParseError> GetNetworkEvent() const
245 : {
246 14 : if (data_.empty()) {
247 1 : return std::unexpected(ParseError::BufferTooSmall);
248 : }
249 :
250 13 : std::span<const char> data{ data_ };
251 : auto parsed = NetworkMessageData<1>::template ParseData<NetworkEvent>(
252 13 : data.subspan(0, std::min(sizeof(NetworkEvent), data.size())));
253 13 : if (!parsed.has_value()) {
254 1 : return std::unexpected(parsed.error());
255 : }
256 12 : auto [network_event] = *parsed;
257 12 : return network_event;
258 : }
259 :
260 : private:
261 : std::vector<char> data_;
262 : };
263 : }; // namespace Soldank
264 :
265 : #endif
|