1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
#pragma once
#include <array>
template <typename T>
using Bytes = std::array<std::byte, sizeof(T)>;
// bit_cast used for going between ulong, float, etc. It's a new C++20 feature
#ifdef __cpp_lib_bit_cast
#include <bit>
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<class To, class From>
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<typename Value, std::enable_if_t<sizeof(Value) == 2, bool> = true>
inline Bytes<Value> HostToNetwork(Value a_Value)
{
UInt16 Bits = bit_cast<UInt16>(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<typename Value, std::enable_if_t<sizeof(Value) == 4, bool> = true>
inline Bytes<Value> HostToNetwork(Value a_Value)
{
UInt32 Bits = bit_cast<UInt32>(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<typename Value, std::enable_if_t<sizeof(Value) == 8, bool> = true>
inline Bytes<Value> HostToNetwork(Value a_Value)
{
UInt64 Bits = bit_cast<UInt64>(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<typename Value, std::enable_if_t<sizeof(Value) == 2, bool> = true>
inline Value NetworkToHost(Bytes<Value> a_Value)
{
UInt16 val = UInt16(
UInt16(a_Value[0]) << 8 |
UInt16(a_Value[1]));
return bit_cast<Value>(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<typename Value, std::enable_if_t<sizeof(Value) == 4, bool> = true>
inline Value NetworkToHost(Bytes<Value> 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<Value>(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<typename Value, std::enable_if_t<sizeof(Value) == 8, bool> = true>
inline Value NetworkToHost(Bytes<Value> 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<Value>(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<typename Value>
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<Value> bytes;
std::copy(a_Mem, a_Mem + sizeof(Value), bytes.begin());
return NetworkToHost<Value>(bytes);
}
|