diff --git a/CMakeLists.txt b/CMakeLists.txt index 710b3a8..c9314d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,10 +62,16 @@ INCLUDE(ExternalProject) # Compilation options # ******************* +# Always check for C++ features +INCLUDE(CheckCXXFeatures) + +IF(HAS_GCC_BUILTIN_SWAP) + SET(HAVE_GCC_BUILTIN_SWAP ON) +ENDIF() + # C++11 support OPTION(LIBTINS_ENABLE_CXX11 "Compile libtins with c++11 features" ON) IF(LIBTINS_ENABLE_CXX11) - INCLUDE(CheckCXXFeatures) # We only use declval and decltype on gcc/clang as VC fails to build that code, # at least on VC2013 IF(HAS_CXX11_RVALUE_REFERENCES AND ((HAS_CXX11_DECLVAL AND HAS_CXX11_DECLTYPE) OR MSVC)) diff --git a/cmake/Modules/CheckCXXFeatures.cmake b/cmake/Modules/CheckCXXFeatures.cmake index 293bb21..5e859ff 100644 --- a/cmake/Modules/CheckCXXFeatures.cmake +++ b/cmake/Modules/CheckCXXFeatures.cmake @@ -142,3 +142,4 @@ cxx11_check_feature("decltype" HAS_CXX11_DECLTYPE) cxx11_check_feature("declval" HAS_CXX11_DECLVAL) cxx11_check_feature("initializer_list" HAS_CXX11_INITIALIZER_LIST) cxx11_check_feature("rvalue-references" HAS_CXX11_RVALUE_REFERENCES) +cxx11_check_feature("builtin-swap" HAS_GCC_BUILTIN_SWAP) diff --git a/cmake/Modules/CheckCXXFeatures/cxx-test-builtin-swap.cpp b/cmake/Modules/CheckCXXFeatures/cxx-test-builtin-swap.cpp new file mode 100644 index 0000000..e4437b7 --- /dev/null +++ b/cmake/Modules/CheckCXXFeatures/cxx-test-builtin-swap.cpp @@ -0,0 +1,8 @@ +#include + +int main() { + uint16_t u16 = __builtin_bswap16(0x9812U); + uint32_t u32 = __builtin_bswap32(0x9812ad81U); + uint64_t u64 = __builtin_bswap64(0x9812ad81f61a890dU); + return (u16 > 0 && u32 > 0 && u64 > 0) ? 0 : 1; +} \ No newline at end of file diff --git a/include/tins/config.h.in b/include/tins/config.h.in index ab521de..8fb34e8 100644 --- a/include/tins/config.h.in +++ b/include/tins/config.h.in @@ -16,4 +16,7 @@ /* Have TCP ACK tracking */ #cmakedefine HAVE_ACK_TRACKER +/* Have GCC builtin swap */ +#cmakedefine HAVE_GCC_BUILTIN_SWAP + #endif // TINS_CONFIG_H diff --git a/include/tins/endianness.h b/include/tins/endianness.h index edd3aa4..d2cc447 100644 --- a/include/tins/endianness.h +++ b/include/tins/endianness.h @@ -42,6 +42,7 @@ #define TINS_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) #define TINS_IS_BIG_ENDIAN (_BYTE_ORDER == _BIG_ENDIAN) #elif defined(_WIN32) + #include // Assume windows == little endian. fixme later #define TINS_IS_LITTLE_ENDIAN 1 #define TINS_IS_BIG_ENDIAN 0 @@ -51,6 +52,19 @@ #define TINS_IS_BIG_ENDIAN (__BYTE_ORDER == __BIG_ENDIAN) #endif +// Define macros to swap bytes using compiler intrinsics when possible +#if defined(_MSC_VER) + #define TINS_BYTE_SWAP_16(data) _byteswap_ushort(data) + #define TINS_BYTE_SWAP_32(data) _byteswap_ulong(data) + #define TINS_BYTE_SWAP_64(data) _byteswap_uint64(data) +#elif defined(HAVE_GCC_BUILTIN_SWAP) + #define TINS_BYTE_SWAP_16(data) __builtin_bswap16(data) + #define TINS_BYTE_SWAP_32(data) __builtin_bswap32(data) + #define TINS_BYTE_SWAP_64(data) __builtin_bswap64(data) +#else + #define TINS_NO_BYTE_SWAP_INTRINSICS +#endif + namespace Tins { namespace Endian { @@ -70,7 +84,11 @@ inline uint8_t do_change_endian(uint8_t data) { * \param data The data to convert. */ inline uint16_t do_change_endian(uint16_t data) { - return ((data & 0xff00) >> 8) | ((data & 0x00ff) << 8); + #ifdef TINS_NO_BYTE_SWAP_INTRINSICS + return ((data & 0xff00) >> 8) | ((data & 0x00ff) << 8); + #else + return TINS_BYTE_SWAP_16(data); + #endif } /** @@ -79,8 +97,12 @@ inline uint16_t do_change_endian(uint16_t data) { * \param data The data to convert. */ inline uint32_t do_change_endian(uint32_t data) { - return (((data & 0xff000000) >> 24) | ((data & 0x00ff0000) >> 8) | - ((data & 0x0000ff00) << 8) | ((data & 0x000000ff) << 24)); + #ifdef TINS_NO_BYTE_SWAP_INTRINSICS + return (((data & 0xff000000) >> 24) | ((data & 0x00ff0000) >> 8) | + ((data & 0x0000ff00) << 8) | ((data & 0x000000ff) << 24)); + #else + return TINS_BYTE_SWAP_32(data); + #endif } /** @@ -89,8 +111,12 @@ inline uint32_t do_change_endian(uint32_t data) { * \param data The data to convert. */ inline uint64_t do_change_endian(uint64_t data) { - return (((uint64_t)(do_change_endian((uint32_t)(data & 0xffffffff))) << 32) | - (do_change_endian(((uint32_t)(data >> 32))))); + #ifdef TINS_NO_BYTE_SWAP_INTRINSICS + return (((uint64_t)(do_change_endian((uint32_t)(data & 0xffffffff))) << 32) | + (do_change_endian(((uint32_t)(data >> 32))))); + #else + return TINS_BYTE_SWAP_64(data); + #endif } /**