Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
a29e8cb6bd | |||
70d721aef3 |
80
README.md
80
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 <Endianness.hpp>
|
||||
|
||||
// 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
|
||||
|
||||
|
@@ -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 <typename T>
|
||||
T ReverseByteOrder(T val)
|
||||
@@ -66,11 +46,43 @@ namespace Endianness
|
||||
pRetVal[size - 1 - i] = pVal[i];
|
||||
}
|
||||
return retVal;
|
||||
|
||||
//return byteswap(val);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void ReverseByteOrder(T* buffer, size_t length)
|
||||
{
|
||||
// Compile-time check to ensure T is an integral type and not bool
|
||||
// (single-byte types like char, int8_t don't need byte reversal)
|
||||
static_assert(std::is_integral_v<T>, "ReverseByteOrder (buffer): Type T must be an integral type.");
|
||||
static_assert(sizeof(T) > 1, "ReverseByteOrder (buffer): Type T must be larger than 1 byte for byte reversal.");
|
||||
|
||||
if (!buffer || length == 0) {
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
buffer[i] = ReverseByteOrder(buffer[i]); // Call the single-value function
|
||||
}
|
||||
}
|
||||
|
||||
#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>(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>(s8 val);;
|
||||
template <> u16 ReverseByteOrder<u16>(u16 val);
|
||||
template <> u32 ReverseByteOrder<u32>(u32 val);
|
||||
|
||||
template <> u64 ReverseByteOrder<u64>(u64 val);
|
||||
template <> s16 ReverseByteOrder<s16>(s16 val);
|
||||
|
||||
template <> s32 ReverseByteOrder<s32>(s32 val);
|
||||
|
||||
template <> s64 ReverseByteOrder<s64>(s64 val);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
template <typename T>
|
||||
T ReverseByteOrderIfLittleEndian(T val)
|
||||
{
|
||||
@@ -80,59 +92,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 <typename T> T HostToNetworkOrder(T host) {throw;}
|
||||
|
||||
template<> inline u16 HostToNetworkOrder<u16>(u16 host) { return HostToNetworkOrder(host);}
|
||||
template<> inline u32 HostToNetworkOrder<u32>(u32 host) { return HostToNetworkOrder(host);}
|
||||
template<> inline u64 HostToNetworkOrder<u64>(u64 host) { return HostToNetworkOrder(host);}
|
||||
template<> inline s16 HostToNetworkOrder<s16>(s16 host) { return HostToNetworkOrder(host);}
|
||||
template<> inline s32 HostToNetworkOrder<s32>(s32 host) { return HostToNetworkOrder(host);}
|
||||
template<> inline s64 HostToNetworkOrder<s64>(s64 host) { return HostToNetworkOrder(host);}
|
||||
template<> inline float HostToNetworkOrder<float>(float host) { return HostToNetworkOrder(host);}
|
||||
template<> inline double HostToNetworkOrder<double>(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 <typename T> T NetworkToHostOrder(T network) { return NetworkToHostOrder(network);}
|
||||
|
||||
template<> inline u16 NetworkToHostOrder<u16>(u16 network) { return NetworkToHostOrder(network);}
|
||||
template<> inline u32 NetworkToHostOrder<u32>(u32 network) { return NetworkToHostOrder(network);}
|
||||
template<> inline u64 NetworkToHostOrder<u64>(u64 network) { return NetworkToHostOrder(network);}
|
||||
template<> inline s16 NetworkToHostOrder<s16>(s16 network) { return NetworkToHostOrder(network);}
|
||||
template<> inline s32 NetworkToHostOrder<s32>(s32 network) { return NetworkToHostOrder(network);}
|
||||
template<> inline s64 NetworkToHostOrder<s64>(s64 network) { return NetworkToHostOrder(network);}
|
||||
template<> inline float NetworkToHostOrder<float>(float network) { return NetworkToHostOrder(network);}
|
||||
template<> inline double NetworkToHostOrder<double>(double network) { return NetworkToHostOrder(network);}
|
||||
|
||||
template <typename T> T HostToNetworkOrder(T host) { return ReverseByteOrderIfLittleEndian(host);}
|
||||
|
||||
template <typename T> T NetworkToHostOrder(T network) { return ReverseByteOrderIfLittleEndian(network);}
|
||||
|
||||
|
||||
|
||||
|
50
main.cpp
50
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<T>) {
|
||||
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<T, float>) {
|
||||
std::cout << *reinterpret_cast<uint32_t*>(&network_value);
|
||||
} else { // double
|
||||
std::cout << *reinterpret_cast<uint64_t*>(&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);
|
||||
@@ -76,16 +64,34 @@ void TestReverseByteOrderIfLittleEndian(const std::string& type_name, T original
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
|
||||
void TestBuffer()
|
||||
{
|
||||
int data[500];
|
||||
|
||||
for (int i = 0; i < 500; i++)
|
||||
{
|
||||
data[i] = i*5;
|
||||
}
|
||||
|
||||
Endianness::ReverseByteOrder(data, 500);
|
||||
|
||||
Endianness::ReverseByteOrder(data, 500);
|
||||
}
|
||||
|
||||
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<uint16_t>("u16", i*i);
|
||||
TestRoundTrip<uint32_t>("u32", i*i*i);
|
||||
TestRoundTrip<uint64_t>("u64", i*i*i*i);
|
||||
TestRoundTrip<float>("float", i / 3.14159f);
|
||||
}
|
||||
|
||||
TestBuffer();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
@@ -13,20 +13,74 @@
|
||||
|
||||
namespace Endianness
|
||||
{
|
||||
template <>
|
||||
u8 ReverseByteOrder<unsigned char>(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<signed char>(s8 val)
|
||||
{return val;}
|
||||
|
||||
template <>
|
||||
u16 ReverseByteOrder<unsigned short>(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<unsigned>(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<unsigned long long>(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<short>(s16 val)
|
||||
{
|
||||
return (val << 8) | ((val >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
template <>
|
||||
s32 ReverseByteOrder<int>(s32 val)
|
||||
{
|
||||
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF );
|
||||
return (val << 16) | ((val >> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
template <>
|
||||
s64 ReverseByteOrder<long long>(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) {
|
||||
|
Reference in New Issue
Block a user