From 871e06c690be2f5689a645ebd9cd4cb6b7675d6b Mon Sep 17 00:00:00 2001 From: Martchus Date: Sat, 4 Feb 2023 23:18:05 +0100 Subject: [PATCH] WIP: Optimize binary conversion --- conversion/binaryconversion.h | 10 ++ conversion/binaryconversionprivate.h | 137 ++++++++++++++++++++------- 2 files changed, 111 insertions(+), 36 deletions(-) diff --git a/conversion/binaryconversion.h b/conversion/binaryconversion.h index 750d1e4..1f4bfa3 100644 --- a/conversion/binaryconversion.h +++ b/conversion/binaryconversion.h @@ -4,6 +4,16 @@ #include "../global.h" #include +#include +#include + +// use std::bit_cast and std::byteswap if available as only GCC is able to optimize the custom bitshift code in all cases +// note: Clang 15.0.0 is only able to optimize the bitshift code in getBytes() functions and MSVC 19.10 is not able to +// optimize it at all. Unfortunately bit_cast and byteswap are only available in C++20 and 23 respectively so the +// custom code needs to stay for compatibility. +#if __cplusplus >= 202002L +#include +#endif // detect byte order according to __BYTE_ORDER__ #if defined(__BYTE_ORDER__) diff --git a/conversion/binaryconversionprivate.h b/conversion/binaryconversionprivate.h index 989c128..fa829a9 100644 --- a/conversion/binaryconversionprivate.h +++ b/conversion/binaryconversionprivate.h @@ -2,10 +2,6 @@ #error "Do not include binaryconversionprivate.h directly." #else -#include "../global.h" - -#include - // disable warnings about sign conversions when using GCC or Clang #ifdef __GNUC__ #pragma GCC diagnostic push @@ -18,10 +14,18 @@ CPP_UTILITIES_EXPORT constexpr std::int16_t toInt16(const char *value) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 +#if defined(__cpp_lib_bit_cast) && defined(__cpp_lib_byteswap) + return std::byteswap(std::bit_cast(value)); +#else return static_cast((static_cast(value[0]) << 8 & 0xFF00) | (static_cast(value[1]) & 0x00FF)); +#endif +#else +#if defined(__cpp_lib_bit_cast) + return std::bit_cast(value); #else return static_cast((static_cast(value[1]) << 8 & 0xFF00) | (static_cast(value[0]) & 0x00FF)); #endif +#endif } /*! @@ -30,10 +34,18 @@ CPP_UTILITIES_EXPORT constexpr std::int16_t toInt16(const char *value) CPP_UTILITIES_EXPORT constexpr std::uint16_t toUInt16(const char *value) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 +#if defined(__cpp_lib_bit_cast) && defined(__cpp_lib_byteswap) + return std::byteswap(std::bit_cast(value)); +#else return static_cast((static_cast(value[0]) << 8 & 0xFF00) | (static_cast(value[1]) & 0x00FF)); +#endif +#else +#if defined(__cpp_lib_bit_cast) + return std::bit_cast(value); #else return static_cast((static_cast(value[1]) << 8 & 0xFF00) | (static_cast(value[0]) & 0x00FF)); #endif +#endif } /*! @@ -42,14 +54,22 @@ CPP_UTILITIES_EXPORT constexpr std::uint16_t toUInt16(const char *value) CPP_UTILITIES_EXPORT constexpr std::int32_t toInt32(const char *value) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 +#if defined(__cpp_lib_bit_cast) && defined(__cpp_lib_byteswap) + return std::byteswap(std::bit_cast(value)); +#else return static_cast((static_cast(value[0]) << 24 & 0xFF000000) | (static_cast(value[1]) << 16 & 0x00FF0000) | (static_cast(value[2]) << 8 & 0x0000FF00) | (static_cast(value[3]) & 0x000000FF)); +#endif +#else +#if defined(__cpp_lib_bit_cast) + return std::bit_cast(value); #else return static_cast((static_cast(value[3]) << 24 & 0xFF000000) | (static_cast(value[2]) << 16 & 0x00FF0000) | (static_cast(value[1]) << 8 & 0x0000FF00) | (static_cast(value[0]) & 0x000000FF)); #endif +#endif } /*! @@ -72,11 +92,32 @@ CPP_UTILITIES_EXPORT constexpr std::uint32_t toUInt24(const char *value) CPP_UTILITIES_EXPORT constexpr std::uint32_t toUInt32(const char *value) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 - return (static_cast(value[0]) << 24 & 0xFF000000) | (static_cast(value[1]) << 16 & 0x00FF0000) - | (static_cast(value[2]) << 8 & 0x0000FF00) | (static_cast(value[3]) & 0x000000FF); +#if __cplusplus >= 202002L + if (std::is_constant_evaluated()) { +#endif + return (static_cast(value[0]) << 24 & 0xFF000000) | (static_cast(value[1]) << 16 & 0x00FF0000) + | (static_cast(value[2]) << 8 & 0x0000FF00) | (static_cast(value[3]) & 0x000000FF); +#if __cplusplus >= 202002L + } else { + auto dst = std::uint32_t(); + std::memcpy(&dst, value, sizeof(dst)); + return dst; + } +#endif #else - return (static_cast(value[3]) << 24 & 0xFF000000) | (static_cast(value[2]) << 16 & 0x00FF0000) - | (static_cast(value[1]) << 8 & 0x0000FF00) | (static_cast(value[0]) & 0x000000FF); +#if __cplusplus >= 202002L + if (std::is_constant_evaluated()) { +#endif + return (static_cast(value[3]) << 24 & 0xFF000000) | (static_cast(value[2]) << 16 & 0x00FF0000) + | (static_cast(value[1]) << 8 & 0x0000FF00) | (static_cast(value[0]) & 0x000000FF); +#if __cplusplus >= 202002L + } else { + + auto dst = std::uint32_t(); + std::memcpy(&dst, value, sizeof(dst)); + return dst; + } +#endif #endif } @@ -86,16 +127,24 @@ CPP_UTILITIES_EXPORT constexpr std::uint32_t toUInt32(const char *value) CPP_UTILITIES_EXPORT constexpr std::int64_t toInt64(const char *value) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 +#if defined(__cpp_lib_bit_cast) && defined(__cpp_lib_byteswap) + return std::byteswap(std::bit_cast(value)); +#else return (static_cast(value[0]) << 56 & 0xFF00000000000000) | (static_cast(value[1]) << 48 & 0x00FF000000000000) | (static_cast(value[2]) << 40 & 0x0000FF0000000000) | (static_cast(value[3]) << 32 & 0x000000FF00000000) | (static_cast(value[4]) << 24 & 0x00000000FF000000) | (static_cast(value[5]) << 16 & 0x0000000000FF0000) | (static_cast(value[6]) << 8 & 0x000000000000FF00) | (static_cast(value[7]) & 0x00000000000000FF); +#endif +#else +#if defined(__cpp_lib_bit_cast) + return std::bit_cast(value); // wrong, this would return pointer value #else return (static_cast(value[7]) << 56 & 0xFF00000000000000) | (static_cast(value[6]) << 48 & 0x00FF000000000000) | (static_cast(value[5]) << 40 & 0x0000FF0000000000) | (static_cast(value[4]) << 32 & 0x000000FF00000000) | (static_cast(value[3]) << 24 & 0x00000000FF000000) | (static_cast(value[2]) << 16 & 0x0000000000FF0000) | (static_cast(value[1]) << 8 & 0x000000000000FF00) | (static_cast(value[0]) & 0x00000000000000FF); #endif +#endif } /*! @@ -104,16 +153,24 @@ CPP_UTILITIES_EXPORT constexpr std::int64_t toInt64(const char *value) CPP_UTILITIES_EXPORT constexpr std::uint64_t toUInt64(const char *value) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 +#if defined(__cpp_lib_bit_cast) && defined(__cpp_lib_byteswap) + return std::byteswap(std::bit_cast(value)); +#else return (static_cast(value[0]) << 56 & 0xFF00000000000000) | (static_cast(value[1]) << 48 & 0x00FF000000000000) | (static_cast(value[2]) << 40 & 0x0000FF0000000000) | (static_cast(value[3]) << 32 & 0x000000FF00000000) | (static_cast(value[4]) << 24 & 0x00000000FF000000) | (static_cast(value[5]) << 16 & 0x0000000000FF0000) | (static_cast(value[6]) << 8 & 0x000000000000FF00) | (static_cast(value[7]) & 0x00000000000000FF); +#endif +#else +#if defined(__cpp_lib_bit_cast) + return std::bit_cast(value); #else return (static_cast(value[7]) << 56 & 0xFF00000000000000) | (static_cast(value[6]) << 48 & 0x00FF000000000000) | (static_cast(value[5]) << 40 & 0x0000FF0000000000) | (static_cast(value[4]) << 32 & 0x000000FF00000000) | (static_cast(value[3]) << 24 & 0x00000000FF000000) | (static_cast(value[2]) << 16 & 0x0000000000FF0000) | (static_cast(value[1]) << 8 & 0x000000000000FF00) | (static_cast(value[0]) & 0x00000000000000FF); #endif +#endif } /*! @@ -142,11 +199,15 @@ CPP_UTILITIES_EXPORT inline double toFloat64(const char *value) CPP_UTILITIES_EXPORT inline void getBytes(std::int16_t value, char *outputbuffer) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 +#if defined(__cpp_lib_byteswap) + value = std::byteswap(value); + std::memcpy(outputbuffer, &value, sizeof(value)); +#else outputbuffer[0] = static_cast((value >> 8) & 0xFF); outputbuffer[1] = static_cast((value)&0xFF); +#endif #else - outputbuffer[1] = static_cast((value >> 8) & 0xFF); - outputbuffer[0] = static_cast((value)&0xFF); + std::memcpy(outputbuffer, &value, sizeof(value)); #endif } @@ -156,11 +217,15 @@ CPP_UTILITIES_EXPORT inline void getBytes(std::int16_t value, char *outputbuffer CPP_UTILITIES_EXPORT inline void getBytes(std::uint16_t value, char *outputbuffer) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 +#if defined(__cpp_lib_byteswap) + value = std::byteswap(value); + std::memcpy(outputbuffer, &value, sizeof(value)); +#else outputbuffer[0] = static_cast((value >> 8) & 0xFF); outputbuffer[1] = static_cast((value)&0xFF); +#endif #else - outputbuffer[1] = static_cast((value >> 8) & 0xFF); - outputbuffer[0] = static_cast((value)&0xFF); + std::memcpy(outputbuffer, &value, sizeof(value)); #endif } @@ -187,15 +252,17 @@ CPP_UTILITIES_EXPORT inline void getBytes24(std::uint32_t value, char *outputbuf CPP_UTILITIES_EXPORT inline void getBytes(std::int32_t value, char *outputbuffer) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 +#if defined(__cpp_lib_byteswap) + value = std::byteswap(value); + std::memcpy(outputbuffer, &value, sizeof(value)); +#else outputbuffer[0] = static_cast((value >> 24) & 0xFF); outputbuffer[1] = static_cast((value >> 16) & 0xFF); outputbuffer[2] = static_cast((value >> 8) & 0xFF); outputbuffer[3] = static_cast((value)&0xFF); +#endif #else - outputbuffer[3] = static_cast((value >> 24) & 0xFF); - outputbuffer[2] = static_cast((value >> 16) & 0xFF); - outputbuffer[1] = static_cast((value >> 8) & 0xFF); - outputbuffer[0] = static_cast((value)&0xFF); + std::memcpy(outputbuffer, &value, sizeof(value)); #endif } @@ -205,15 +272,17 @@ CPP_UTILITIES_EXPORT inline void getBytes(std::int32_t value, char *outputbuffer CPP_UTILITIES_EXPORT inline void getBytes(std::uint32_t value, char *outputbuffer) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 +#if defined(__cpp_lib_byteswap) + value = std::byteswap(value); + std::memcpy(outputbuffer, &value, sizeof(value)); +#else outputbuffer[0] = static_cast((value >> 24) & 0xFF); outputbuffer[1] = static_cast((value >> 16) & 0xFF); outputbuffer[2] = static_cast((value >> 8) & 0xFF); outputbuffer[3] = static_cast((value)&0xFF); +#endif #else - outputbuffer[3] = static_cast((value >> 24) & 0xFF); - outputbuffer[2] = static_cast((value >> 16) & 0xFF); - outputbuffer[1] = static_cast((value >> 8) & 0xFF); - outputbuffer[0] = static_cast((value)&0xFF); + std::memcpy(outputbuffer, &value, sizeof(value)); #endif } @@ -223,6 +292,10 @@ CPP_UTILITIES_EXPORT inline void getBytes(std::uint32_t value, char *outputbuffe CPP_UTILITIES_EXPORT inline void getBytes(std::int64_t value, char *outputbuffer) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 +#if defined(__cpp_lib_byteswap) + value = std::byteswap(value); + std::memcpy(outputbuffer, &value, sizeof(value)); +#else outputbuffer[0] = static_cast((value >> 56) & 0xFF); outputbuffer[1] = static_cast((value >> 48) & 0xFF); outputbuffer[2] = static_cast((value >> 40) & 0xFF); @@ -231,15 +304,9 @@ CPP_UTILITIES_EXPORT inline void getBytes(std::int64_t value, char *outputbuffer outputbuffer[5] = static_cast((value >> 16) & 0xFF); outputbuffer[6] = static_cast((value >> 8) & 0xFF); outputbuffer[7] = static_cast((value)&0xFF); +#endif #else - outputbuffer[7] = static_cast((value >> 56) & 0xFF); - outputbuffer[6] = static_cast((value >> 48) & 0xFF); - outputbuffer[5] = static_cast((value >> 40) & 0xFF); - outputbuffer[4] = static_cast((value >> 32) & 0xFF); - outputbuffer[3] = static_cast((value >> 24) & 0xFF); - outputbuffer[2] = static_cast((value >> 16) & 0xFF); - outputbuffer[1] = static_cast((value >> 8) & 0xFF); - outputbuffer[0] = static_cast((value)&0xFF); + std::memcpy(outputbuffer, &value, sizeof(value)); #endif } @@ -249,6 +316,10 @@ CPP_UTILITIES_EXPORT inline void getBytes(std::int64_t value, char *outputbuffer CPP_UTILITIES_EXPORT inline void getBytes(std::uint64_t value, char *outputbuffer) { #if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0 +#if defined(__cpp_lib_byteswap) + value = std::byteswap(value); + std::memcpy(outputbuffer, &value, sizeof(value)); +#else outputbuffer[0] = static_cast((value >> 56) & 0xFF); outputbuffer[1] = static_cast((value >> 48) & 0xFF); outputbuffer[2] = static_cast((value >> 40) & 0xFF); @@ -257,15 +328,9 @@ CPP_UTILITIES_EXPORT inline void getBytes(std::uint64_t value, char *outputbuffe outputbuffer[5] = static_cast((value >> 16) & 0xFF); outputbuffer[6] = static_cast((value >> 8) & 0xFF); outputbuffer[7] = static_cast((value)&0xFF); +#endif #else - outputbuffer[7] = static_cast((value >> 56) & 0xFF); - outputbuffer[6] = static_cast((value >> 48) & 0xFF); - outputbuffer[5] = static_cast((value >> 40) & 0xFF); - outputbuffer[4] = static_cast((value >> 32) & 0xFF); - outputbuffer[3] = static_cast((value >> 24) & 0xFF); - outputbuffer[2] = static_cast((value >> 16) & 0xFF); - outputbuffer[1] = static_cast((value >> 8) & 0xFF); - outputbuffer[0] = static_cast((value)&0xFF); + std::memcpy(outputbuffer, &value, sizeof(value)); #endif }