/ src / network_serialization.hpp
network_serialization.hpp
  1  // SPDX-FileCopyrightText: Copyright (C) 2025 Marek Küthe <m.k@mk16.de>
  2  //
  3  // SPDX-License-Identifier: GPL-3.0-or-later
  4  
  5  #ifndef NETWORK_SERIALIZATION
  6  #define NETWORK_SERIALIZATION
  7  
  8  #include <algorithm>
  9  #include <array>
 10  #include <bit>
 11  #include <concepts>
 12  #include <cstdint>
 13  #include <boost/asio.hpp>
 14  
 15  namespace NetworkSerialization
 16  {
 17      template<std::integral T>
 18      [[gnu::const]] constexpr T to_bigendian(const T host)
 19          requires(std::endian::native == std::endian::big ||
 20                   std::endian::native == std::endian::little)
 21      {
 22          if constexpr (std::endian::native == std::endian::little)
 23          {
 24              return std::byteswap(host);
 25          }
 26          else
 27          {
 28              return host;
 29          }
 30      }
 31  
 32      template<std::integral T>
 33      [[gnu::const]] constexpr T to_littleendian(const T host)
 34          requires(std::endian::native == std::endian::big ||
 35                   std::endian::native == std::endian::little)
 36      {
 37          if constexpr (std::endian::native == std::endian::big)
 38          {
 39              return std::byteswap(host);
 40          }
 41          else
 42          {
 43              return host;
 44          }
 45      }
 46  
 47      template<typename T>
 48      [[gnu::const]] constexpr std::array<unsigned char, sizeof(T)>
 49          to_byte_array(const T i)
 50          requires(std::is_trivially_copyable_v<T>)
 51      {
 52          return std::bit_cast<std::array<unsigned char, sizeof(T)>>(i);
 53      }
 54  
 55      [[gnu::pure]] inline std::array<unsigned char, 128>
 56          ipv6_to_sockaddr_storage(const boost::asio::ip::address_v6& address,
 57                                   const uint16_t port)
 58      {
 59          std::array<unsigned char, 128> result = {};
 60          result.fill('\0');
 61  
 62          auto * it = result.begin();
 63  
 64          /* 10 */
 65          constexpr uint16_t ipv6_type_int = 10;
 66          constexpr std::array<unsigned char, 2> ipv6_type =
 67              to_byte_array(to_littleendian(ipv6_type_int));
 68          it = std::ranges::copy(ipv6_type, it).out;
 69  
 70          /* P */
 71          const std::array<unsigned char, 2> port_bytes =
 72              to_byte_array(to_bigendian(port));
 73          it = std::ranges::copy(port_bytes, it).out;
 74  
 75          std::advance(it, 4);
 76  
 77          /* IPv6 */
 78          const std::array<unsigned char, 16> ipv6_bytes = address.to_bytes();
 79          std::ranges::copy(ipv6_bytes, it);
 80  
 81          return result;
 82      }
 83  
 84      [[gnu::pure]] inline std::array<unsigned char, 128>
 85          ipv4_to_sockaddr_storage(const boost::asio::ip::address_v4& address,
 86                                   const uint16_t port)
 87      {
 88          std::array<unsigned char, 128> result = {};
 89          result.fill('\0');
 90  
 91          auto * it = result.begin();
 92  
 93          /* 2 */
 94          constexpr uint16_t ipv4_type_int = 2;
 95          constexpr std::array<unsigned char, 2> ipv4_type =
 96              to_byte_array(to_littleendian(ipv4_type_int));
 97          it = std::ranges::copy(ipv4_type, it).out;
 98  
 99          /* P */
100          const std::array<unsigned char, 2> port_bytes =
101              to_byte_array(to_bigendian(port));
102          it = std::ranges::copy(port_bytes, it).out;
103  
104          std::advance(it, 4);
105  
106          /* IPv4 */
107          const std::array<unsigned char, 4> ipv4_bytes = address.to_bytes();
108          std::ranges::copy(ipv4_bytes, it);
109  
110          return result;
111      }
112  } // namespace NetworkSerialization
113  
114  #endif