diff --git a/README.md b/README.md index e69de29..2517f15 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,80 @@ +# Endianness Library + +A modern C++ library for handling endianness conversions and byte order operations. This library provides a robust set of tools for working with different byte orders in a platform-independent manner. + +## Features + +- Platform-independent endianness detection +- Byte order conversion utilities +- Network byte order (big-endian) conversion functions +- Support for various data types: + - Integer types (8, 16, 32, 64-bit, signed and unsigned) + - Floating-point types (float, double) +- Modern C++20 implementation +- Cross-platform support (Windows and Unix-like systems) + +## Requirements + +- C++20 compatible compiler +- CMake 3.18 or higher + +## Building the Project + +```bash +# Create a build directory +mkdir build && cd build + +# Configure with CMake +cmake .. + +# Build the project +cmake --build . +``` + +## Usage + +The library provides several key functions for endianness operations: + +```cpp +#include + +// Check system endianness +bool isLittleEndian = Endianness::IsLittleEndian(); +bool isBigEndian = Endianness::IsBigEndian(); + +// Convert between host and network byte order +uint16_t networkValue = Endianness::HostToNetworkOrder(hostValue); +uint16_t hostValue = Endianness::NetworkToHostOrder(networkValue); + +// Reverse byte order +uint32_t reversed = Endianness::ReverseByteOrder(originalValue); + +// Conditional byte order reversal +uint64_t conditionalReversed = Endianness::ReverseByteOrderIfLittleEndian(value); +``` + +## Supported Types + +The library supports the following types for byte order operations: + +- Unsigned integers: `u8`, `u16`, `u32`, `u64` +- Signed integers: `s8`, `s16`, `s32`, `s64` +- Floating-point: `float`, `double` + +## Project Structure + +``` +. +├── include/ +│ └── Endianness.hpp # Main library header +├── src/ # Implementation files +├── main.cpp # Test program +└── CMakeLists.txt # Build configuration +``` + +## License + +This project is released into the public domain under the Unlicense. See the [LICENSE](LICENSE) file for details. + +## Contributing + diff --git a/include/Endianness.hpp b/include/Endianness.hpp index 6a50907..fc3370a 100644 --- a/include/Endianness.hpp +++ b/include/Endianness.hpp @@ -18,41 +18,21 @@ namespace IntegerLiterals using s32 = int32_t; using sl = s32; using s64 = int64_t; using sd = s64; - constexpr inline u8 operator ""_u8(unsigned long long int value) { return value;} - constexpr inline ub operator ""_ub(unsigned long long int value) { return value;} - constexpr inline u16 operator ""_u16(unsigned long long int value) { return value;} - constexpr inline us operator ""_us(unsigned long long int value) { return value;} - constexpr inline u32 operator ""_u32(unsigned long long int value) { return value;} - constexpr inline u32 operator ""_ul(unsigned long long int value) { return value;} - constexpr inline u64 operator ""_u64(unsigned long long int value) { return value;} - constexpr inline u64 operator ""_ud(unsigned long long int value) { return value;} - - constexpr inline s8 operator ""_s8(long double value) { return value;} - constexpr inline s8 operator ""_sb(long double value) { return value;} - constexpr inline s16 operator ""_s16(long double value) { return value;} - constexpr inline s16 operator ""_ss(long double value) { return value;} - constexpr inline s32 operator ""_s32(long double value) { return value;} - constexpr inline s32 operator ""_sl(long double value) { return value;} - constexpr inline s64 operator ""_s64(long double value) { return value;} - constexpr inline s64 operator ""_sd(long double value) { return value;} } -using namespace IntegerLiterals; - /// https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html /// Platform-independent functions for swapping the byte order of common data types. namespace Endianness { + using namespace IntegerLiterals; + /// Returns true if the host machine is big-endian. + constexpr bool IsBigEndian() noexcept { return (std::endian::native == std::endian::big); } - // TODO: It seems these can't have their implementation put into - // a separate .cpp file else the compiler throws undefined reference - // is it because they're constexpr? - constexpr bool IsBigEndian() { return (std::endian::native == std::endian::big); } - - constexpr bool IsLittleEndian() { return (std::endian::native == std::endian::little); } + /// Returns true if the host machine is little-endian. + constexpr bool IsLittleEndian() noexcept { return (std::endian::native == std::endian::little); } template T ReverseByteOrder(T val) @@ -66,10 +46,24 @@ namespace Endianness pRetVal[size - 1 - i] = pVal[i]; } return retVal; - - //return byteswap(val); } +#pragma region Template Specializations + /// This function simply returns the input value, as no byte order can be swapped on a single-byte-length value. + template <> u8 ReverseByteOrder(u8 val); + /// This function simply returns the input value, as no byte order can be swapped on a single-byte-length value. + template <> s8 ReverseByteOrder(s8 val);; + template <> u16 ReverseByteOrder(u16 val); + template <> u32 ReverseByteOrder(u32 val); + + template <> u64 ReverseByteOrder(u64 val); + template <> s16 ReverseByteOrder(s16 val); + + template <> s32 ReverseByteOrder(s32 val); + + template <> s64 ReverseByteOrder(s64 val); + +#pragma endregion template T ReverseByteOrderIfLittleEndian(T val) @@ -80,59 +74,9 @@ namespace Endianness } - /// Returns the given values converted into network byte order. - /// Generally on x86, numbers are stored in 'little-endian' order. - /// Network order is 'big-endian'. Supposing a big-endian host machine, - /// no conversion will take place and the original value will be returned. - - - - u16 HostToNetworkOrder(u16 host); - u32 HostToNetworkOrder(u32 host); - u64 HostToNetworkOrder(u64 host); - s16 HostToNetworkOrder(s16 host); - s32 HostToNetworkOrder(s32 host); - s64 HostToNetworkOrder(s64 host); - float HostToNetworkOrder(float host); - double HostToNetworkOrder(double host); - - template T HostToNetworkOrder(T host) {throw;} - - template<> inline u16 HostToNetworkOrder(u16 host) { return HostToNetworkOrder(host);} - template<> inline u32 HostToNetworkOrder(u32 host) { return HostToNetworkOrder(host);} - template<> inline u64 HostToNetworkOrder(u64 host) { return HostToNetworkOrder(host);} - template<> inline s16 HostToNetworkOrder(s16 host) { return HostToNetworkOrder(host);} - template<> inline s32 HostToNetworkOrder(s32 host) { return HostToNetworkOrder(host);} - template<> inline s64 HostToNetworkOrder(s64 host) { return HostToNetworkOrder(host);} - template<> inline float HostToNetworkOrder(float host) { return HostToNetworkOrder(host);} - template<> inline double HostToNetworkOrder(double host) { return HostToNetworkOrder(host);} - - /// Returns the given values converted into host-byte order. - /// On x86, this will usually be 'little-endian' - /// On ARM (and other RISC architectures), it is often big-endian. - /// Supposing a big-endian host machine, no conversion will take place and the original value will be returned. - u16 NetworkToHostOrder(u16 network); - u32 NetworkToHostOrder(u32 network); - u64 NetworkToHostOrder(u64 network); - s16 NetworkToHostOrder(s16 network); - s32 NetworkToHostOrder(s32 network); - s64 NetworkToHostOrder(s64 network); - float NetworkToHostOrder(float network); - double NetworkToHostOrder(double network); - - - template T NetworkToHostOrder(T network) { return NetworkToHostOrder(network);} - - template<> inline u16 NetworkToHostOrder(u16 network) { return NetworkToHostOrder(network);} - template<> inline u32 NetworkToHostOrder(u32 network) { return NetworkToHostOrder(network);} - template<> inline u64 NetworkToHostOrder(u64 network) { return NetworkToHostOrder(network);} - template<> inline s16 NetworkToHostOrder(s16 network) { return NetworkToHostOrder(network);} - template<> inline s32 NetworkToHostOrder(s32 network) { return NetworkToHostOrder(network);} - template<> inline s64 NetworkToHostOrder(s64 network) { return NetworkToHostOrder(network);} - template<> inline float NetworkToHostOrder(float network) { return NetworkToHostOrder(network);} - template<> inline double NetworkToHostOrder(double network) { return NetworkToHostOrder(network);} - + template T HostToNetworkOrder(T host) { return ReverseByteOrderIfLittleEndian(host);} + template T NetworkToHostOrder(T network) { return ReverseByteOrderIfLittleEndian(network);} diff --git a/main.cpp b/main.cpp index 19d7758..8585328 100644 --- a/main.cpp +++ b/main.cpp @@ -14,21 +14,9 @@ void TestRoundTrip(const std::string& type_name, T original_value) { std::cout << "Testing " << type_name << ": " << std::endl; - if constexpr (std::is_floating_point_v) { - std::cout << " Original: " << original_value << std::endl; - std::cout << " Network (raw bits): 0x" << std::hex << std::setw(sizeof(T) * 2) << std::setfill('0'); - if constexpr (std::is_same_v) { - std::cout << *reinterpret_cast(&network_value); - } else { // double - std::cout << *reinterpret_cast(&network_value); - } - std::cout << std::dec << std::endl; - std::cout << " Host: " << host_value << std::endl; - } else { - std::cout << " Original: 0x" << std::hex << original_value << std::dec << std::endl; - std::cout << " Network: 0x" << std::hex << network_value << std::dec << std::endl; - std::cout << " Host: 0x" << std::hex << host_value << std::dec << std::endl; - } + std::cout << " Original: " << std::hex << original_value << std::dec << std::endl; + std::cout << " Network: " << std::hex << network_value << std::dec << std::endl; + std::cout << " Host: " << std::hex << host_value << std::dec << std::endl; if (original_value == host_value) { @@ -45,8 +33,8 @@ void TestReverseByteOrder(const std::string& type_name, T original_value) { T reversed_value = Endianness::ReverseByteOrder(original_value); std::cout << "Testing ReverseByteOrder for " << type_name << ": " << std::endl; - std::cout << " Original: 0x" << std::hex << original_value << std::dec << std::endl; - std::cout << " Reversed: 0x" << std::hex << reversed_value << std::dec << std::endl; + std::cout << " Original: " << std::hex << original_value << std::dec << std::endl; + std::cout << " Reversed: " << std::hex << reversed_value << std::dec << std::endl; std::cout << std::endl; } @@ -56,8 +44,8 @@ void TestReverseByteOrderIfLittleEndian(const std::string& type_name, T original T conditional_reversed_value = Endianness::ReverseByteOrderIfLittleEndian(original_value); std::cout << "Testing ReverseByteOrderIfLittleEndian for " << type_name << ": " << std::endl; - std::cout << " Original: 0x" << std::hex << original_value << std::dec << std::endl; - std::cout << " Conditional Reversed: 0x" << std::hex << conditional_reversed_value << std::dec << std::endl; + std::cout << " Original: " << std::hex << original_value << std::dec << std::endl; + std::cout << " Conditional Reversed: " << std::hex << conditional_reversed_value << std::dec << std::endl; if (Endianness::IsLittleEndian()) { T expected_reversed = Endianness::ReverseByteOrder(original_value); @@ -81,9 +69,10 @@ int main() for (int i = 0; i < 256; i++) { TestRoundTrip("u8", i); - TestRoundTrip("u16", i*i); - TestRoundTrip("u32", i*i*i); - TestRoundTrip("u64", i*i*i*i); + TestRoundTrip("u16", i*i); + TestRoundTrip("u32", i*i*i); + TestRoundTrip("u64", i*i*i*i); + TestRoundTrip("float", i / 3.14159f); } return 0; diff --git a/src/Endianness.cpp b/src/Endianness.cpp index 4969cce..37e8429 100644 --- a/src/Endianness.cpp +++ b/src/Endianness.cpp @@ -13,20 +13,74 @@ namespace Endianness { + template <> + u8 ReverseByteOrder(u8 val) + { return val;} - // TODO: Manually swap bytes so we can depend less on clunky include directives. - u16 HostToNetworkOrder(u16 host) { return ReverseByteOrderIfLittleEndian(host); } - u16 NetworkToHostOrder(u16 network) { return ReverseByteOrderIfLittleEndian(network); } - s16 HostToNetworkOrder(s16 host) { return ReverseByteOrderIfLittleEndian( host); } - s16 NetworkToHostOrder(s16 network) { return ReverseByteOrderIfLittleEndian(network);} - u32 HostToNetworkOrder(u32 host) { return ReverseByteOrderIfLittleEndian(host); } - u32 NetworkToHostOrder(u32 network) { return ReverseByteOrderIfLittleEndian(network); } - s32 HostToNetworkOrder(s32 host) { return ReverseByteOrderIfLittleEndian(host);} - s32 NetworkToHostOrder(s32 network) { return ReverseByteOrderIfLittleEndian(network);} - u64 HostToNetworkOrder(u64 host) { return ReverseByteOrderIfLittleEndian(host); } - u64 NetworkToHostOrder(u64 network) { return ReverseByteOrderIfLittleEndian(network); } - s64 HostToNetworkOrder(s64 host) { return ReverseByteOrderIfLittleEndian(host); } - s64 NetworkToHostOrder(s64 network) { return ReverseByteOrderIfLittleEndian(network); } + template <> + s8 ReverseByteOrder(s8 val) + {return val;} + + template <> + u16 ReverseByteOrder(u16 val) + { +#if defined(_MSC_VER) + return _byteswap_ushort(val); +#elif defined(__GNUC__) + return __builtin_bswap16(val); +#endif + return (val << 8) | (val >> 8); + } + + template <> + u32 ReverseByteOrder(u32 val) + { +#ifdef _MSC_VER + return _byteswap_ulong(val); +#endif + +#ifdef __GNUC__ + return __builtin_bswap32(val); +#endif + + val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); + return (val << 16) | (val >> 16); + } + + template <> + u64 ReverseByteOrder(u64 val) + { +#if defined(_MSC_VER) + return _byteswap_uint64(val); +#elif defined(__GNUC__) + return __builtin_bswap64(val); +#else + val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); + val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); + return (val << 32) | (val >> 32); +#endif + } + + template <> + s16 ReverseByteOrder(s16 val) + { + return (val << 8) | ((val >> 8) & 0xFF); + } + + template <> + s32 ReverseByteOrder(s32 val) + { + val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); + return (val << 16) | ((val >> 16) & 0xFFFF); + } + + template <> + s64 ReverseByteOrder(s64 val) + { + val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); + val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); + return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); + } float HostToNetworkOrder(float host) {