// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // endian_utils.hpp // Endian conversion utilities // // This file provides utility functions that support exchanging data between // environments with different endianness. // // Main features: // - Endianness detection // - Byte-swap functions // - Cross-platform conversion helpers #ifndef SANGI_ENDIAN_UTILS_HPP #define SANGI_ENDIAN_UTILS_HPP #include #include #include // Compiler-dependent endianness detection #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define SANGI_LITTLE_ENDIAN 1 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define SANGI_BIG_ENDIAN 1 #else #error "Unknown endianness" #endif #elif defined(_MSC_VER) // Microsoft Visual C++ is always little-endian #define SANGI_LITTLE_ENDIAN 1 #else // Runtime detection fallback #warning "Compile-time endianness detection not available, using runtime detection" #define SANGI_RUNTIME_ENDIAN_DETECTION 1 #endif namespace sangi { namespace endian { /** * @brief Endianness enumeration */ enum class Endianness { Little, ///< Little-endian (least significant byte first) Big ///< Big-endian (most significant byte first) }; /** * @brief Endianness of the current platform */ #if defined(SANGI_LITTLE_ENDIAN) constexpr Endianness platform_endianness = Endianness::Little; #elif defined(SANGI_BIG_ENDIAN) constexpr Endianness platform_endianness = Endianness::Big; #else // Runtime detection required inline Endianness detect_platform_endianness() { const uint16_t test = 0x0102; const uint8_t* ptr = reinterpret_cast(&test); return (ptr[0] == 0x02) ? Endianness::Little : Endianness::Big; } const Endianness platform_endianness = detect_platform_endianness(); #endif /** * @brief Reverse the byte order of a 16-bit integer * @param value Value to reverse * @return The value with its bytes reversed */ inline uint16_t byte_swap(uint16_t value) { return ((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8); } /** * @brief Reverse the byte order of a 32-bit integer * @param value Value to reverse * @return The value with its bytes reversed */ inline uint32_t byte_swap(uint32_t value) { return ((value & 0x000000FF) << 24) | ((value & 0x0000FF00) << 8) | ((value & 0x00FF0000) >> 8) | ((value & 0xFF000000) >> 24); } /** * @brief Reverse the byte order of a 64-bit integer * @param value Value to reverse * @return The value with its bytes reversed */ inline uint64_t byte_swap(uint64_t value) { return ((value & 0x00000000000000FFULL) << 56) | ((value & 0x000000000000FF00ULL) << 40) | ((value & 0x0000000000FF0000ULL) << 24) | ((value & 0x00000000FF000000ULL) << 8) | ((value & 0x000000FF00000000ULL) >> 8) | ((value & 0x0000FF0000000000ULL) >> 24) | ((value & 0x00FF000000000000ULL) >> 40) | ((value & 0xFF00000000000000ULL) >> 56); } /** * @brief Reverse the byte order of a signed integer (uses the unsigned version) * @param value Value to reverse * @return The value with its bytes reversed */ template requires std::is_signed_v inline T byte_swap(T value) { using UT = typename std::make_unsigned::type; return static_cast(byte_swap(static_cast(value))); } /** * @brief Convert from a specific endianness to the current platform's endianness * @param value Value to convert * @param from Source endianness * @return Converted value */ template inline T to_native(T value, Endianness from) { if (from == platform_endianness) { return value; } return byte_swap(value); } /** * @brief Convert from the current platform's endianness to a specific endianness * @param value Value to convert * @param to Destination endianness * @return Converted value */ template inline T from_native(T value, Endianness to) { if (to == platform_endianness) { return value; } return byte_swap(value); } /** * @brief Convert from little-endian to the current platform's endianness * @param value Value to convert * @return Converted value */ template inline T from_little_endian(T value) { return to_native(value, Endianness::Little); } /** * @brief Convert from big-endian to the current platform's endianness * @param value Value to convert * @return Converted value */ template inline T from_big_endian(T value) { return to_native(value, Endianness::Big); } /** * @brief Convert from the current platform's endianness to little-endian * @param value Value to convert * @return Converted value */ template inline T to_little_endian(T value) { return from_native(value, Endianness::Little); } /** * @brief Convert from the current platform's endianness to big-endian * @param value Value to convert * @return Converted value */ template inline T to_big_endian(T value) { return from_native(value, Endianness::Big); } /** * @brief Reverse the byte order of a buffer * @param buffer Buffer to reverse * @param size Buffer size in bytes * @param element_size Size of each element in bytes */ inline void byte_swap_buffer(void* buffer, size_t size, size_t element_size) { if (buffer == nullptr || size == 0 || element_size <= 1) { return; } uint8_t* bytes = static_cast(buffer); for (size_t i = 0; i < size; i += element_size) { for (size_t j = 0; j < element_size / 2; ++j) { std::swap(bytes[i + j], bytes[i + element_size - 1 - j]); } } } } // namespace endian } // namespace sangi #endif // SANGI_ENDIAN_UTILS_HPP