TRLC Platform Library  1.0.0
Header-only C++ library for compile-time platform detection and abstraction
endianness.hpp
Go to the documentation of this file.
1 #pragma once
2 
22 #include <cstdint>
23 #include <type_traits>
24 
25 namespace trlc {
26 namespace platform {
27 
33 enum class ByteOrder : int {
34  unknown = 0,
36  big_endian,
38 };
39 
50 
56  constexpr bool isNativeOrder(ByteOrder order) const noexcept { return byte_order == order; }
57 
63  constexpr bool needsByteSwap(ByteOrder target_order) const noexcept {
64  if (byte_order == ByteOrder::unknown || target_order == ByteOrder::unknown) {
65  return false; // Conservative: assume no swap needed for unknown
66  }
67  return byte_order != target_order;
68  }
69 };
70 
71 namespace detail {
72 
79 constexpr ByteOrder detectByteOrderCompileTime() noexcept {
80 // Try standard endianness macros first
81 #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)
82  #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
84  #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
85  return ByteOrder::big_endian;
86  #else
88  #endif
89 
90 // Try BSD-style macros
91 #elif defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
92  #if BYTE_ORDER == LITTLE_ENDIAN
94  #elif BYTE_ORDER == BIG_ENDIAN
95  return ByteOrder::big_endian;
96  #else
98  #endif
99 
100 // Try Windows-specific detection
101 #elif defined(_WIN32) || defined(_WIN64)
102  // Windows is always little-endian on supported architectures
104 
105 // Try architecture-specific detection
106 #elif defined(__LITTLE_ENDIAN__) || defined(__ARMEL__)
108 #elif defined(__BIG_ENDIAN__) || defined(__ARMEB__)
109  return ByteOrder::big_endian;
110 
111 // Architecture-specific knowledge
112 #elif defined(__i386__) || defined(__x86_64__) || defined(__amd64__) || defined(_M_IX86) || \
113  defined(_M_X64) || defined(_M_AMD64) || defined(__aarch64__) || defined(_M_ARM64)
114  // x86, x86_64, and ARM64 are little-endian
116 
117 #elif defined(__sparc__) || defined(__sparc) || defined(__MIPSEB__) || defined(_POWER) || \
118  defined(__powerpc__) && defined(__BIG_ENDIAN__)
119  // SPARC, big-endian MIPS, PowerPC in big-endian mode
120  return ByteOrder::big_endian;
121 
122 #else
123  // Unable to determine at compile time
124  return ByteOrder::unknown;
125 #endif
126 }
127 
135  union {
136  uint32_t integer;
137  uint8_t bytes[4];
138  } test = {0x01020304};
139 
140  if (test.bytes[0] == 0x01) {
141  return ByteOrder::big_endian;
142  } else if (test.bytes[0] == 0x04) {
144  } else {
146  }
147 }
148 
149 } // namespace detail
150 
159 constexpr ByteOrder getByteOrder() noexcept {
160  constexpr ByteOrder compile_time_order = detail::detectByteOrderCompileTime();
161 
162  if constexpr (compile_time_order != ByteOrder::unknown) {
163  return compile_time_order;
164  } else {
165  // Note: This branch won't be constexpr but allows fallback
167  }
168 }
169 
178 constexpr EndiannessInfo getEndiannessInfo() noexcept {
179  constexpr ByteOrder order = getByteOrder();
180  return EndiannessInfo{order, order == ByteOrder::little_endian, order == ByteOrder::big_endian};
181 }
182 
188 constexpr bool isLittleEndian() noexcept {
190 }
191 
197 constexpr bool isBigEndian() noexcept {
199 }
200 
201 // =============================================================================
202 // Byte Swapping Utilities
203 // =============================================================================
204 
205 namespace detail {
206 
212 constexpr uint16_t byteSwap16Impl(uint16_t value) noexcept {
213 #if defined(_MSC_VER)
214  // MSVC has _byteswap_ushort but it's not constexpr
215  // Use manual implementation for constexpr compatibility
216  return static_cast<uint16_t>((value << 8) | (value >> 8));
217 #elif defined(__GNUC__) || defined(__clang__)
218  // GCC/Clang builtin (available as constexpr in recent versions)
219  #if __has_builtin(__builtin_bswap16)
220  return __builtin_bswap16(value);
221  #else
222  return static_cast<uint16_t>((value << 8) | (value >> 8));
223  #endif
224 #else
225  // Manual implementation for unknown compilers
226  return static_cast<uint16_t>((value << 8) | (value >> 8));
227 #endif
228 }
229 
233 constexpr uint32_t byteSwap32Impl(uint32_t value) noexcept {
234 #if defined(_MSC_VER)
235  // Manual implementation for constexpr compatibility
236  return ((value & 0xFF000000) >> 24) | ((value & 0x00FF0000) >> 8) |
237  ((value & 0x0000FF00) << 8) | ((value & 0x000000FF) << 24);
238 #elif defined(__GNUC__) || defined(__clang__)
239  #if __has_builtin(__builtin_bswap32)
240  return __builtin_bswap32(value);
241  #else
242  return ((value & 0xFF000000) >> 24) | ((value & 0x00FF0000) >> 8) |
243  ((value & 0x0000FF00) << 8) | ((value & 0x000000FF) << 24);
244  #endif
245 #else
246  return ((value & 0xFF000000) >> 24) | ((value & 0x00FF0000) >> 8) |
247  ((value & 0x0000FF00) << 8) | ((value & 0x000000FF) << 24);
248 #endif
249 }
250 
254 constexpr uint64_t byteSwap64Impl(uint64_t value) noexcept {
255 #if defined(_MSC_VER)
256  // Manual implementation for constexpr compatibility
257  return ((value & 0xFF00000000000000ULL) >> 56) | ((value & 0x00FF000000000000ULL) >> 40) |
258  ((value & 0x0000FF0000000000ULL) >> 24) | ((value & 0x000000FF00000000ULL) >> 8) |
259  ((value & 0x00000000FF000000ULL) << 8) | ((value & 0x0000000000FF0000ULL) << 24) |
260  ((value & 0x000000000000FF00ULL) << 40) | ((value & 0x00000000000000FFULL) << 56);
261 #elif defined(__GNUC__) || defined(__clang__)
262  #if __has_builtin(__builtin_bswap64)
263  return __builtin_bswap64(value);
264  #else
265  return ((value & 0xFF00000000000000ULL) >> 56) | ((value & 0x00FF000000000000ULL) >> 40) |
266  ((value & 0x0000FF0000000000ULL) >> 24) | ((value & 0x000000FF00000000ULL) >> 8) |
267  ((value & 0x00000000FF000000ULL) << 8) | ((value & 0x0000000000FF0000ULL) << 24) |
268  ((value & 0x000000000000FF00ULL) << 40) | ((value & 0x00000000000000FFULL) << 56);
269  #endif
270 #else
271  return ((value & 0xFF00000000000000ULL) >> 56) | ((value & 0x00FF000000000000ULL) >> 40) |
272  ((value & 0x0000FF0000000000ULL) >> 24) | ((value & 0x000000FF00000000ULL) >> 8) |
273  ((value & 0x00000000FF000000ULL) << 8) | ((value & 0x0000000000FF0000ULL) << 24) |
274  ((value & 0x000000000000FF00ULL) << 40) | ((value & 0x00000000000000FFULL) << 56);
275 #endif
276 }
277 
278 } // namespace detail
279 
286 constexpr uint16_t byteSwap16(uint16_t value) noexcept {
287  return detail::byteSwap16Impl(value);
288 }
289 
296 constexpr uint32_t byteSwap32(uint32_t value) noexcept {
297  return detail::byteSwap32Impl(value);
298 }
299 
306 constexpr uint64_t byteSwap64(uint64_t value) noexcept {
307  return detail::byteSwap64Impl(value);
308 }
309 
320 template <typename Type>
321 constexpr Type byteSwap(Type value) noexcept {
322  static_assert(std::is_integral_v<Type>, "byteSwap only supports integral types");
323 
324  if constexpr (sizeof(Type) == 1) {
325  // Single byte - no swapping needed
326  return value;
327  } else if constexpr (sizeof(Type) == 2) {
328  // 16-bit types
329  using UnsignedType = std::make_unsigned_t<Type>;
330  auto unsigned_value = static_cast<UnsignedType>(value);
331  auto swapped = byteSwap16(static_cast<uint16_t>(unsigned_value));
332  return static_cast<Type>(swapped);
333  } else if constexpr (sizeof(Type) == 4) {
334  // 32-bit types
335  using UnsignedType = std::make_unsigned_t<Type>;
336  auto unsigned_value = static_cast<UnsignedType>(value);
337  auto swapped = byteSwap32(static_cast<uint32_t>(unsigned_value));
338  return static_cast<Type>(swapped);
339  } else if constexpr (sizeof(Type) == 8) {
340  // 64-bit types
341  using UnsignedType = std::make_unsigned_t<Type>;
342  auto unsigned_value = static_cast<UnsignedType>(value);
343  auto swapped = byteSwap64(static_cast<uint64_t>(unsigned_value));
344  return static_cast<Type>(swapped);
345  } else {
346  static_assert(sizeof(Type) <= 8, "byteSwap supports types up to 64 bits");
347  return value; // Should never reach here
348  }
349 }
350 
351 // =============================================================================
352 // Network Byte Order Conversion
353 // =============================================================================
354 
365 template <typename Type>
366 constexpr Type hostToNetwork(Type value) noexcept {
367  static_assert(std::is_integral_v<Type>, "hostToNetwork only supports integral types");
368 
369  if constexpr (isLittleEndian()) {
370  // Host is little-endian, need to swap to big-endian
371  return byteSwap(value);
372  } else {
373  // Host is big-endian or unknown, no conversion needed
374  return value;
375  }
376 }
377 
388 template <typename Type>
389 constexpr Type networkToHost(Type value) noexcept {
390  static_assert(std::is_integral_v<Type>, "networkToHost only supports integral types");
391 
392  if constexpr (isLittleEndian()) {
393  // Host is little-endian, need to swap from big-endian
394  return byteSwap(value);
395  } else {
396  // Host is big-endian or unknown, no conversion needed
397  return value;
398  }
399 }
400 
401 // =============================================================================
402 // Specialized Network Conversion Functions
403 // =============================================================================
404 
413 constexpr uint16_t hostToNetworkShort(uint16_t hostshort) noexcept {
414  return hostToNetwork(hostshort);
415 }
416 
425 constexpr uint32_t hostToNetworkLong(uint32_t hostlong) noexcept {
426  return hostToNetwork(hostlong);
427 }
428 
437 constexpr uint16_t networkToHostShort(uint16_t netshort) noexcept {
438  return networkToHost(netshort);
439 }
440 
449 constexpr uint32_t networkToHostLong(uint32_t netlong) noexcept {
450  return networkToHost(netlong);
451 }
452 
453 // =============================================================================
454 // Utility Functions
455 // =============================================================================
456 
464 constexpr bool areByteOrdersCompatible(ByteOrder order1, ByteOrder order2) noexcept {
465  // Unknown orders are considered compatible (conservative approach)
466  if (order1 == ByteOrder::unknown || order2 == ByteOrder::unknown) {
467  return true;
468  }
469  return order1 == order2;
470 }
471 
478 constexpr ByteOrder getOppositeByteOrder(ByteOrder order) noexcept {
479  switch (order) {
481  return ByteOrder::big_endian;
484  default:
485  return ByteOrder::unknown;
486  }
487 }
488 
498 template <typename Type>
499 constexpr Type convertByteOrder(Type value, ByteOrder from_order, ByteOrder to_order) noexcept {
500  static_assert(std::is_integral_v<Type>, "convertByteOrder only supports integral types");
501 
502  if (areByteOrdersCompatible(from_order, to_order)) {
503  return value;
504  } else {
505  return byteSwap(value);
506  }
507 }
508 
509 } // namespace platform
510 } // namespace trlc
511 
512 // =============================================================================
513 // Convenience Macros
514 // =============================================================================
515 
521 #define TRLC_ENDIAN_LITTLE (trlc::platform::isLittleEndian())
522 
528 #define TRLC_ENDIAN_BIG (trlc::platform::isBigEndian())
529 
538 #define TRLC_BYTE_SWAP(x) (trlc::platform::byteSwap(x))
539 
546 #define TRLC_HTON(x) (trlc::platform::hostToNetwork(x))
547 
554 #define TRLC_NTOH(x) (trlc::platform::networkToHost(x))
555 
564 #define TRLC_HTONS(x) (trlc::platform::hostToNetworkShort(x))
565 
574 #define TRLC_HTONL(x) (trlc::platform::hostToNetworkLong(x))
575 
584 #define TRLC_NTOHS(x) (trlc::platform::networkToHostShort(x))
585 
594 #define TRLC_NTOHL(x) (trlc::platform::networkToHostLong(x))
595 
596 // Mark this header as successfully included
597 #define TRLC_ENDIANNESS_INCLUDED
598 
599 // =============================================================================
600 // End of endianness.hpp
601 // =============================================================================
ByteOrder detectByteOrderRuntime()
Runtime endianness detection using union method.
Definition: endianness.hpp:134
static uint32_t byteSwap32Impl(uint32_t value)
32-bit byte swap implementation
Definition: endianness.hpp:233
static uint64_t byteSwap64Impl(uint64_t value)
64-bit byte swap implementation
Definition: endianness.hpp:254
static ByteOrder detectByteOrderCompileTime()
Compile-time endianness detection using preprocessor macros.
Definition: endianness.hpp:79
static uint16_t byteSwap16Impl(uint16_t value)
16-bit byte swap implementation
Definition: endianness.hpp:212
ByteOrder
Byte order enumeration.
Definition: endianness.hpp:33
@ mixed_endian
Mixed endianness (rare architectures)
@ little_endian
Little-endian (LSB first)
@ big_endian
Big-endian (MSB first)
@ unknown
Unknown byte order.
static uint64_t byteSwap64(uint64_t value)
Swap bytes in a 64-bit value.
Definition: endianness.hpp:306
static uint32_t byteSwap32(uint32_t value)
Swap bytes in a 32-bit value.
Definition: endianness.hpp:296
static ByteOrder getOppositeByteOrder(ByteOrder order)
Get the opposite byte order.
Definition: endianness.hpp:478
static EndiannessInfo getEndiannessInfo()
Get comprehensive endianness information.
Definition: endianness.hpp:178
static Type byteSwap(Type value)
Generic byte swap function for integral types.
Definition: endianness.hpp:321
static Type networkToHost(Type value)
Convert from network byte order (big-endian) to host byte order.
Definition: endianness.hpp:389
static uint16_t hostToNetworkShort(uint16_t hostshort)
Convert 16-bit value from host to network byte order.
Definition: endianness.hpp:413
static bool isBigEndian()
Check if system is big-endian.
Definition: endianness.hpp:197
static uint32_t networkToHostLong(uint32_t netlong)
Convert 32-bit value from network to host byte order.
Definition: endianness.hpp:449
static uint32_t hostToNetworkLong(uint32_t hostlong)
Convert 32-bit value from host to network byte order.
Definition: endianness.hpp:425
static ByteOrder getByteOrder()
Get the system's byte order.
Definition: endianness.hpp:159
static uint16_t networkToHostShort(uint16_t netshort)
Convert 16-bit value from network to host byte order.
Definition: endianness.hpp:437
static Type hostToNetwork(Type value)
Convert from host byte order to network byte order (big-endian)
Definition: endianness.hpp:366
static Type convertByteOrder(Type value, ByteOrder from_order, ByteOrder to_order)
Convert value between specified byte orders.
Definition: endianness.hpp:499
static uint16_t byteSwap16(uint16_t value)
Swap bytes in a 16-bit value.
Definition: endianness.hpp:286
static bool isLittleEndian()
Check if system is little-endian.
Definition: endianness.hpp:188
static bool areByteOrdersCompatible(ByteOrder order1, ByteOrder order2)
Check if two byte orders are compatible (no swapping needed)
Definition: endianness.hpp:464
Comprehensive endianness information structure.
Definition: endianness.hpp:46
static bool isNativeOrder(ByteOrder order) const
Check if given byte order matches native order.
Definition: endianness.hpp:56
bool is_big_endian
True if system is big-endian.
Definition: endianness.hpp:49
ByteOrder byte_order
The detected byte order.
Definition: endianness.hpp:47
bool is_little_endian
True if system is little-endian.
Definition: endianness.hpp:48
static bool needsByteSwap(ByteOrder target_order) const
Check if byte swapping is needed for target order.
Definition: endianness.hpp:63