LCOV - code coverage report
Current view: top level - communication - NetworkMessage.hpp (source / functions) Coverage Total Hit
Test: coverage-src.info Lines: 94.4 % 107 101
Test Date: 2025-12-21 01:16:59 Functions: 100.0 % 65 65

            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
        

Generated by: LCOV version 2.0-1