Avoid relying on compiler optimizations for binary conversions

* Add/update binary conversion functions to use `std::memcopy`
* Only GCC could actually optimize the custom code using bit operations
    * Clang could only optimize the `getBytes()` functions
    * MSVC could not optimize any of the functions
* The to…Int…() functions cannot be updated as they are `constexpr` (and
  thus `std::memcopy` and `reinterpret_cast` cannot be used). So they stay
  as-is and `toInt()` has been added instead
* The `BinaryReader` class has been updated to use the new `toInt()`
  function
This commit is contained in:
Martchus 2023-02-05 21:29:22 +01:00
parent 147d36a578
commit 07e9546855
3 changed files with 46 additions and 128 deletions

View File

@ -5,6 +5,7 @@
#include "../misc/traits.h"
#include <cstdint>
#include <cstring>
// use helpers from bits header if available instead of custom code using bit operations
#if __cplusplus >= 202002L

View File

@ -133,31 +133,21 @@ CPP_UTILITIES_EXPORT inline double toFloat64(const char *value)
}
/*!
* \brief Stores the specified 16-bit signed integer value at a specified position in a char array.
* \brief Returns the specified (unsigned) integer converted from the specified char array.
* \remarks
* - The \a value must point to a sequence of characters that is at least as long as the specified integer type.
* - This function is potentially faster than the width-specific toInt() because the width-specific functions use
* custom code that may not be optimized by the compiler. (Only GCC optimized it but not Clang and MSVC.) Note
* that the width-specific functions cannot be changed as they are constexpr and thus cannot use memcpy().
*/
CPP_UTILITIES_EXPORT inline void getBytes(std::int16_t value, char *outputbuffer)
template <class T, Traits::EnableIf<std::is_integral<T>> * = nullptr> CPP_UTILITIES_EXPORT inline T toInt(const char *value)
{
auto dst = T();
std::memcpy(&dst, value, sizeof(T));
#if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0
outputbuffer[0] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[1] = static_cast<char>((value)&0xFF);
#else
outputbuffer[1] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[0] = static_cast<char>((value)&0xFF);
#endif
}
/*!
* \brief Stores the specified 16-bit unsigned integer value at a specified position in a char array.
*/
CPP_UTILITIES_EXPORT inline void getBytes(std::uint16_t value, char *outputbuffer)
{
#if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0
outputbuffer[0] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[1] = static_cast<char>((value)&0xFF);
#else
outputbuffer[1] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[0] = static_cast<char>((value)&0xFF);
dst = swapOrder(dst);
#endif
return dst;
}
/*!
@ -178,91 +168,18 @@ CPP_UTILITIES_EXPORT inline void getBytes24(std::uint32_t value, char *outputbuf
}
/*!
* \brief Stores the specified 32-bit signed integer value at a specified position in a char array.
* \brief Stores the specified (unsigned) integer value in a char array.
* \remarks
* - The \a value outputbuffer must point to a sequence of characters that is at least as long as the specified integer type.
* - This function is potentially faster than the width-specific toInt() because the width-specific functions use
* custom code that may not be optimized by the compiler. (Only GCC optimized it but not Clang and MSVC.)
*/
CPP_UTILITIES_EXPORT inline void getBytes(std::int32_t value, char *outputbuffer)
template <class T, Traits::EnableIf<std::is_integral<T>> * = nullptr> CPP_UTILITIES_EXPORT inline void getBytes(T value, char *outputbuffer)
{
#if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0
outputbuffer[0] = static_cast<char>((value >> 24) & 0xFF);
outputbuffer[1] = static_cast<char>((value >> 16) & 0xFF);
outputbuffer[2] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[3] = static_cast<char>((value)&0xFF);
#else
outputbuffer[3] = static_cast<char>((value >> 24) & 0xFF);
outputbuffer[2] = static_cast<char>((value >> 16) & 0xFF);
outputbuffer[1] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[0] = static_cast<char>((value)&0xFF);
#endif
}
/*!
* \brief Stores the specified 32-bit signed integer value at a specified position in a char array.
*/
CPP_UTILITIES_EXPORT inline void getBytes(std::uint32_t value, char *outputbuffer)
{
#if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0
outputbuffer[0] = static_cast<char>((value >> 24) & 0xFF);
outputbuffer[1] = static_cast<char>((value >> 16) & 0xFF);
outputbuffer[2] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[3] = static_cast<char>((value)&0xFF);
#else
outputbuffer[3] = static_cast<char>((value >> 24) & 0xFF);
outputbuffer[2] = static_cast<char>((value >> 16) & 0xFF);
outputbuffer[1] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[0] = static_cast<char>((value)&0xFF);
#endif
}
/*!
* \brief Stores the specified 64-bit signed integer value at a specified position in a char array.
*/
CPP_UTILITIES_EXPORT inline void getBytes(std::int64_t value, char *outputbuffer)
{
#if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0
outputbuffer[0] = static_cast<char>((value >> 56) & 0xFF);
outputbuffer[1] = static_cast<char>((value >> 48) & 0xFF);
outputbuffer[2] = static_cast<char>((value >> 40) & 0xFF);
outputbuffer[3] = static_cast<char>((value >> 32) & 0xFF);
outputbuffer[4] = static_cast<char>((value >> 24) & 0xFF);
outputbuffer[5] = static_cast<char>((value >> 16) & 0xFF);
outputbuffer[6] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[7] = static_cast<char>((value)&0xFF);
#else
outputbuffer[7] = static_cast<char>((value >> 56) & 0xFF);
outputbuffer[6] = static_cast<char>((value >> 48) & 0xFF);
outputbuffer[5] = static_cast<char>((value >> 40) & 0xFF);
outputbuffer[4] = static_cast<char>((value >> 32) & 0xFF);
outputbuffer[3] = static_cast<char>((value >> 24) & 0xFF);
outputbuffer[2] = static_cast<char>((value >> 16) & 0xFF);
outputbuffer[1] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[0] = static_cast<char>((value)&0xFF);
#endif
}
/*!
* \brief Stores the specified 64-bit unsigned integer value at a specified position in a char array.
*/
CPP_UTILITIES_EXPORT inline void getBytes(std::uint64_t value, char *outputbuffer)
{
#if CONVERSION_UTILITIES_BINARY_CONVERSION_INTERNAL == 0
outputbuffer[0] = static_cast<char>((value >> 56) & 0xFF);
outputbuffer[1] = static_cast<char>((value >> 48) & 0xFF);
outputbuffer[2] = static_cast<char>((value >> 40) & 0xFF);
outputbuffer[3] = static_cast<char>((value >> 32) & 0xFF);
outputbuffer[4] = static_cast<char>((value >> 24) & 0xFF);
outputbuffer[5] = static_cast<char>((value >> 16) & 0xFF);
outputbuffer[6] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[7] = static_cast<char>((value)&0xFF);
#else
outputbuffer[7] = static_cast<char>((value >> 56) & 0xFF);
outputbuffer[6] = static_cast<char>((value >> 48) & 0xFF);
outputbuffer[5] = static_cast<char>((value >> 40) & 0xFF);
outputbuffer[4] = static_cast<char>((value >> 32) & 0xFF);
outputbuffer[3] = static_cast<char>((value >> 24) & 0xFF);
outputbuffer[2] = static_cast<char>((value >> 16) & 0xFF);
outputbuffer[1] = static_cast<char>((value >> 8) & 0xFF);
outputbuffer[0] = static_cast<char>((value)&0xFF);
value = swapOrder(value);
#endif
std::memcpy(outputbuffer, &value, sizeof(T));
}
/*!

View File

@ -243,7 +243,7 @@ inline void BinaryReader::read(std::vector<char> &buffer, std::streamsize length
inline std::int16_t BinaryReader::readInt16BE()
{
m_stream->read(m_buffer, sizeof(std::int16_t));
return BE::toInt16(m_buffer);
return BE::toInt<std::int16_t>(m_buffer);
}
/*!
@ -252,7 +252,7 @@ inline std::int16_t BinaryReader::readInt16BE()
inline std::uint16_t BinaryReader::readUInt16BE()
{
m_stream->read(m_buffer, sizeof(std::uint16_t));
return BE::toUInt16(m_buffer);
return BE::toInt<std::uint16_t>(m_buffer);
}
/*!
@ -262,7 +262,7 @@ inline std::int32_t BinaryReader::readInt24BE()
{
*m_buffer = 0;
m_stream->read(m_buffer + 1, 3);
auto val = BE::toInt32(m_buffer);
auto val = BE::toInt<std::int32_t>(m_buffer);
if (val >= 0x800000) {
val = -(0x1000000 - val);
}
@ -276,7 +276,7 @@ inline std::uint32_t BinaryReader::readUInt24BE()
{
*m_buffer = 0;
m_stream->read(m_buffer + 1, 3);
return BE::toUInt32(m_buffer);
return BE::toInt<std::uint32_t>(m_buffer);
}
/*!
@ -285,7 +285,7 @@ inline std::uint32_t BinaryReader::readUInt24BE()
inline std::int32_t BinaryReader::readInt32BE()
{
m_stream->read(m_buffer, sizeof(std::int32_t));
return BE::toInt32(m_buffer);
return BE::toInt<std::int32_t>(m_buffer);
}
/*!
@ -294,7 +294,7 @@ inline std::int32_t BinaryReader::readInt32BE()
inline std::uint32_t BinaryReader::readUInt32BE()
{
m_stream->read(m_buffer, sizeof(std::uint32_t));
return BE::toUInt32(m_buffer);
return BE::toInt<std::uint32_t>(m_buffer);
}
/*!
@ -304,7 +304,7 @@ inline std::int64_t BinaryReader::readInt40BE()
{
*m_buffer = *(m_buffer + 1) = *(m_buffer + 2) = 0;
m_stream->read(m_buffer + 3, 5);
auto val = BE::toInt64(m_buffer);
auto val = BE::toInt<std::int64_t>(m_buffer);
if (val >= 0x8000000000) {
val = -(0x10000000000 - val);
}
@ -318,7 +318,7 @@ inline std::uint64_t BinaryReader::readUInt40BE()
{
*m_buffer = *(m_buffer + 1) = *(m_buffer + 2) = 0;
m_stream->read(m_buffer + 3, 5);
return BE::toUInt64(m_buffer);
return BE::toInt<std::uint64_t>(m_buffer);
}
/*!
@ -328,7 +328,7 @@ inline std::int64_t BinaryReader::readInt56BE()
{
*m_buffer = 0;
m_stream->read(m_buffer + 1, 7);
auto val = BE::toInt64(m_buffer);
auto val = BE::toInt<std::int64_t>(m_buffer);
if (val >= 0x80000000000000) {
val = -(0x100000000000000 - val);
}
@ -342,7 +342,7 @@ inline std::uint64_t BinaryReader::readUInt56BE()
{
*m_buffer = 0;
m_stream->read(m_buffer + 1, 7);
return BE::toUInt64(m_buffer);
return BE::toInt<std::uint64_t>(m_buffer);
}
/*!
@ -351,7 +351,7 @@ inline std::uint64_t BinaryReader::readUInt56BE()
inline std::int64_t BinaryReader::readInt64BE()
{
m_stream->read(m_buffer, sizeof(std::int64_t));
return BE::toInt64(m_buffer);
return BE::toInt<std::int64_t>(m_buffer);
}
/*!
@ -360,7 +360,7 @@ inline std::int64_t BinaryReader::readInt64BE()
inline std::uint64_t BinaryReader::readUInt64BE()
{
m_stream->read(m_buffer, sizeof(std::uint64_t));
return BE::toUInt64(m_buffer);
return BE::toInt<std::uint64_t>(m_buffer);
}
/*!
@ -370,7 +370,7 @@ inline std::uint64_t BinaryReader::readUInt64BE()
inline std::uint64_t BinaryReader::readVariableLengthUIntBE()
{
bufferVariableLengthInteger();
return BE::toUInt64(m_buffer);
return BE::toInt<std::uint64_t>(m_buffer);
}
/*!
@ -397,7 +397,7 @@ inline double BinaryReader::readFloat64BE()
inline std::int16_t BinaryReader::readInt16LE()
{
m_stream->read(m_buffer, sizeof(std::int16_t));
return LE::toInt16(m_buffer);
return LE::toInt<std::int16_t>(m_buffer);
}
/*!
@ -406,7 +406,7 @@ inline std::int16_t BinaryReader::readInt16LE()
inline std::uint16_t BinaryReader::readUInt16LE()
{
m_stream->read(m_buffer, sizeof(std::uint16_t));
return LE::toUInt16(m_buffer);
return LE::toInt<std::uint16_t>(m_buffer);
}
/*!
@ -416,7 +416,7 @@ inline std::int32_t BinaryReader::readInt24LE()
{
*(m_buffer + 3) = 0;
m_stream->read(m_buffer, 3);
auto val = LE::toInt32(m_buffer);
auto val = LE::toInt<std::int32_t>(m_buffer);
if (val >= 0x800000) {
val = -(0x1000000 - val);
}
@ -430,7 +430,7 @@ inline std::uint32_t BinaryReader::readUInt24LE()
{
*(m_buffer + 3) = 0;
m_stream->read(m_buffer, 3);
return LE::toUInt32(m_buffer);
return LE::toInt<std::uint32_t>(m_buffer);
}
/*!
@ -439,7 +439,7 @@ inline std::uint32_t BinaryReader::readUInt24LE()
inline std::int32_t BinaryReader::readInt32LE()
{
m_stream->read(m_buffer, sizeof(std::int32_t));
return LE::toInt32(m_buffer);
return LE::toInt<std::int32_t>(m_buffer);
}
/*!
@ -448,7 +448,7 @@ inline std::int32_t BinaryReader::readInt32LE()
inline std::uint32_t BinaryReader::readUInt32LE()
{
m_stream->read(m_buffer, sizeof(std::uint32_t));
return LE::toUInt32(m_buffer);
return LE::toInt<std::uint32_t>(m_buffer);
}
/*!
@ -458,7 +458,7 @@ inline std::int64_t BinaryReader::readInt40LE()
{
*(m_buffer + 5) = *(m_buffer + 6) = *(m_buffer + 7) = 0;
m_stream->read(m_buffer, 5);
auto val = LE::toInt64(m_buffer);
auto val = LE::toInt<std::int64_t>(m_buffer);
if (val >= 0x8000000000) {
val = -(0x10000000000 - val);
}
@ -472,7 +472,7 @@ inline std::uint64_t BinaryReader::readUInt40LE()
{
*(m_buffer + 5) = *(m_buffer + 6) = *(m_buffer + 7) = 0;
m_stream->read(m_buffer, 5);
return LE::toUInt64(m_buffer);
return LE::toInt<std::uint64_t>(m_buffer);
}
/*!
@ -482,7 +482,7 @@ inline std::int64_t BinaryReader::readInt56LE()
{
*(m_buffer + 7) = 0;
m_stream->read(m_buffer, 7);
auto val = LE::toInt64(m_buffer);
auto val = LE::toInt<std::int64_t>(m_buffer);
if (val >= 0x80000000000000) {
val = -(0x100000000000000 - val);
}
@ -496,7 +496,7 @@ inline std::uint64_t BinaryReader::readUInt56LE()
{
*(m_buffer + 7) = 0;
m_stream->read(m_buffer, 7);
return LE::toUInt64(m_buffer);
return LE::toInt<std::uint64_t>(m_buffer);
}
/*!
@ -505,7 +505,7 @@ inline std::uint64_t BinaryReader::readUInt56LE()
inline std::int64_t BinaryReader::readInt64LE()
{
m_stream->read(m_buffer, sizeof(std::int64_t));
return LE::toInt64(m_buffer);
return LE::toInt<std::int64_t>(m_buffer);
}
/*!
@ -514,7 +514,7 @@ inline std::int64_t BinaryReader::readInt64LE()
inline std::uint64_t BinaryReader::readUInt64LE()
{
m_stream->read(m_buffer, sizeof(std::uint64_t));
return LE::toUInt64(m_buffer);
return LE::toInt<std::uint64_t>(m_buffer);
}
/*!
@ -524,7 +524,7 @@ inline std::uint64_t BinaryReader::readUInt64LE()
inline std::uint64_t BinaryReader::readVariableLengthUIntLE()
{
bufferVariableLengthInteger();
return LE::toUInt64(m_buffer);
return LE::toInt<std::uint64_t>(m_buffer);
}
/*!