#pragma once #include template using Bytes = std::array; // bit_cast used for going between ulong, float, etc. It's a new C++20 feature #ifdef __cpp_lib_bit_cast #include using std::bit_cast; // Fallback in case we're using C++17 #else // bit-for-bit convert one type to another. In C++ the only non-UB way to do this is *memcpy*. Nearly every other // option is a strict aliasing violation. template std::enable_if_t< sizeof(To) == sizeof(From), To> bit_cast(const From &src) noexcept { To dst; std::memcpy(&dst, &src, sizeof(To)); return dst; } #endif /** Converts a 16-bit host integer or float value to bytes in big-endian (Network) order. @tparam Value The host 16-bit type (Int16, UInt16). Usually inferred. @param a_Value The input integer or float value. @return The resulting bytes. */ template = true> inline Bytes HostToNetwork(Value a_Value) { UInt16 Bits = bit_cast(a_Value); return { std::byte(Bits >> 8), std::byte(Bits) }; } /** Converts a 32-bit host integer or float value to bytes in big-endian (Network) order. @tparam Value The host 32-bit type (Int32, UInt32, float). Usually inferred. @param a_Value The input integer or float value. @return The resulting bytes. */ template = true> inline Bytes HostToNetwork(Value a_Value) { UInt32 Bits = bit_cast(a_Value); return { std::byte(Bits >> 24), std::byte(Bits >> 16), std::byte(Bits >> 8), std::byte(Bits) }; } /** Converts a 64-bit host integer or float value to bytes in big-endian (Network) order. @tparam Value The host 64-bit type (Int64, UInt64, double). Usually inferred. @param a_Value The input integer or float value. @return The resulting bytes. */ template = true> inline Bytes HostToNetwork(Value a_Value) { UInt64 Bits = bit_cast(a_Value); return { std::byte(Bits >> 56), std::byte(Bits >> 48), std::byte(Bits >> 40), std::byte(Bits >> 32), std::byte(Bits >> 24), std::byte(Bits >> 16), std::byte(Bits >> 8), std::byte(Bits) }; } /** Reads a 16-bit integer or float value from big-endian (Network) bytes. @tparam Value The desired 16-bit type (Int16, UInt16) @param a_Value The input bytes. @return The resulting integer or float value. */ template = true> inline Value NetworkToHost(Bytes a_Value) { UInt16 val = UInt16( UInt16(a_Value[0]) << 8 | UInt16(a_Value[1])); return bit_cast(val); } /** Reads a 32-bit integer or float value from big-endian (Network) bytes. @tparam Value The desired 32-bit type (Int32, UInt32, float) @param a_Value The input bytes. @return The resulting integer or float value. */ template = true> inline Value NetworkToHost(Bytes a_Value) { UInt32 val = UInt32( UInt32(a_Value[0]) << 24 | UInt32(a_Value[1]) << 16 | UInt32(a_Value[2]) << 8 | UInt32(a_Value[3])); return bit_cast(val); } /** Reads a 64-bit integer or float value from big-endian (Network) bytes. @tparam Value The desired 64-bit type (Int64, UInt64, double) @param a_Value The input bytes. @return The resulting integer or float value. */ template = true> inline Value NetworkToHost(Bytes a_Value) { UInt64 val = UInt64( UInt64(a_Value[0]) << 56 | UInt64(a_Value[1]) << 48 | UInt64(a_Value[2]) << 40 | UInt64(a_Value[3]) << 32 | UInt64(a_Value[4]) << 24 | UInt64(a_Value[5]) << 16 | UInt64(a_Value[6]) << 8 | UInt64(a_Value[7])); return bit_cast(val); } /** Reads an integer or float type from its big-endian (Network) bytes. @tparam Value The desired result type (Int16 / 32 / 64, UInt16 / 32 / 64, float, double). @param a_Mem A pointer to the first input byte. Length is inferred from the result type. @return The resulting integer or float value. Consider using NetworkToHost when the data is owned since it provides additional type safety (length is known). */ template inline Value NetworkBufToHost(const std::byte* a_Mem) { // Copy unfortunately needed to add the length information required by the rest of the API. // Gets completely optimised out in my testing. Bytes bytes; std::copy(a_Mem, a_Mem + sizeof(Value), bytes.begin()); return NetworkToHost(bytes); }