From a632b57e7a8db4ab3e3e2bd133d87b8530f51ed6 Mon Sep 17 00:00:00 2001 From: jules Date: Mon, 26 Mar 2018 12:25:02 +0100 Subject: [PATCH] Simplified ByteOrder, and made it less susceptible to UB when reading from non-aligned addresses. Removed ambiguous method ByteOrder::littleEndianInt (c1, c2, c3, c4), which was probably producing the wrong behaviour on big-endian systems --- .../codecs/juce_WavAudioFormat.cpp | 4 +- modules/juce_core/memory/juce_ByteOrder.h | 243 ++++++++---------- .../app_properties/juce_PropertiesFile.cpp | 4 +- 3 files changed, 116 insertions(+), 135 deletions(-) diff --git a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 0f0c6f2195..1954b8b89f 100644 --- a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -157,8 +157,8 @@ const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info"; //============================================================================== namespace WavFileHelpers { - inline int chunkName (const char* const name) noexcept { return (int) ByteOrder::littleEndianInt (name[0], name[1], name[2], name[3]); } - inline size_t roundUpSize (size_t sz) noexcept { return (sz + 3) & ~3u; } + JUCE_CONSTEXPR inline int chunkName (const char* name) noexcept { return (int) ByteOrder::littleEndianInt (name); } + JUCE_CONSTEXPR inline size_t roundUpSize (size_t sz) noexcept { return (sz + 3) & ~3u; } #if JUCE_MSVC #pragma pack (push, 1) diff --git a/modules/juce_core/memory/juce_ByteOrder.h b/modules/juce_core/memory/juce_ByteOrder.h index 2d11572cc0..95e28482fb 100644 --- a/modules/juce_core/memory/juce_ByteOrder.h +++ b/modules/juce_core/memory/juce_ByteOrder.h @@ -36,117 +36,121 @@ public: /** Swaps the upper and lower bytes of a 16-bit integer. */ JUCE_CONSTEXPR static uint16 swap (uint16 value) noexcept; + /** Swaps the upper and lower bytes of a 16-bit integer. */ + JUCE_CONSTEXPR static int16 swap (int16 value) noexcept; + /** Reverses the order of the 4 bytes in a 32-bit integer. */ static uint32 swap (uint32 value) noexcept; + /** Reverses the order of the 4 bytes in a 32-bit integer. */ + static int32 swap (int32 value) noexcept; + /** Reverses the order of the 8 bytes in a 64-bit integer. */ static uint64 swap (uint64 value) noexcept; - //============================================================================== - /** Swaps the byte order of a 16-bit unsigned int if the CPU is big-endian */ - JUCE_CONSTEXPR static uint16 swapIfBigEndian (uint16 value) noexcept; - - /** Swaps the byte order of a 32-bit unsigned int if the CPU is big-endian */ - static uint32 swapIfBigEndian (uint32 value) noexcept; - - /** Swaps the byte order of a 64-bit unsigned int if the CPU is big-endian */ - static uint64 swapIfBigEndian (uint64 value) noexcept; - - /** Swaps the byte order of a 16-bit signed int if the CPU is big-endian */ - JUCE_CONSTEXPR static int16 swapIfBigEndian (int16 value) noexcept; - - /** Swaps the byte order of a 32-bit signed int if the CPU is big-endian */ - static int32 swapIfBigEndian (int32 value) noexcept; - - /** Swaps the byte order of a 64-bit signed int if the CPU is big-endian */ - static int64 swapIfBigEndian (int64 value) noexcept; - - /** Swaps the byte order of a 32-bit float if the CPU is big-endian */ - static float swapIfBigEndian (float value) noexcept; - - /** Swaps the byte order of a 64-bit float if the CPU is big-endian */ - static double swapIfBigEndian (double value) noexcept; - - /** Swaps the byte order of a 16-bit unsigned int if the CPU is little-endian */ - JUCE_CONSTEXPR static uint16 swapIfLittleEndian (uint16 value) noexcept; - - /** Swaps the byte order of a 32-bit unsigned int if the CPU is little-endian */ - static uint32 swapIfLittleEndian (uint32 value) noexcept; - - /** Swaps the byte order of a 64-bit unsigned int if the CPU is little-endian */ - static uint64 swapIfLittleEndian (uint64 value) noexcept; - - /** Swaps the byte order of a 16-bit signed int if the CPU is little-endian */ - JUCE_CONSTEXPR static int16 swapIfLittleEndian (int16 value) noexcept; - - /** Swaps the byte order of a 32-bit signed int if the CPU is little-endian */ - static int32 swapIfLittleEndian (int32 value) noexcept; + /** Reverses the order of the 8 bytes in a 64-bit integer. */ + static int64 swap (int64 value) noexcept; - /** Swaps the byte order of a 64-bit signed int if the CPU is little-endian */ - static int64 swapIfLittleEndian (int64 value) noexcept; + /** Returns a garbled float which has the reverse byte-order of the original. */ + static float swap (float value) noexcept; - /** Swaps the byte order of a 32-bit float if the CPU is little-endian */ - static float swapIfLittleEndian (float value) noexcept; + /** Returns a garbled double which has the reverse byte-order of the original. */ + static double swap (double value) noexcept; - /** Swaps the byte order of a 64-bit float if the CPU is little-endian */ - static double swapIfLittleEndian (double value) noexcept; + //============================================================================== + /** Swaps the byte order of a signed or unsigned integer if the CPU is big-endian */ + template + static Type swapIfBigEndian (Type value) noexcept + { + #if JUCE_LITTLE_ENDIAN + return value; + #else + return swap (value); + #endif + } + + /** Swaps the byte order of a signed or unsigned integer if the CPU is little-endian */ + template + static Type swapIfLittleEndian (Type value) noexcept + { + #if JUCE_LITTLE_ENDIAN + return swap (value); + #else + return value; + #endif + } //============================================================================== /** Turns 4 bytes into a little-endian integer. */ - static uint32 littleEndianInt (const void* bytes) noexcept; - - /** Turns 4 characters into a little-endian integer. */ - JUCE_CONSTEXPR static uint32 littleEndianInt (char c1, char c2, char c3, char c4) noexcept; + JUCE_CONSTEXPR static uint32 littleEndianInt (const void* bytes) noexcept; /** Turns 8 bytes into a little-endian integer. */ - static uint64 littleEndianInt64 (const void* bytes) noexcept; + JUCE_CONSTEXPR static uint64 littleEndianInt64 (const void* bytes) noexcept; /** Turns 2 bytes into a little-endian integer. */ - static uint16 littleEndianShort (const void* bytes) noexcept; + JUCE_CONSTEXPR static uint16 littleEndianShort (const void* bytes) noexcept; + + /** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ + JUCE_CONSTEXPR static int littleEndian24Bit (const void* bytes) noexcept; + + /** Copies a 24-bit number to 3 little-endian bytes. */ + static void littleEndian24BitToChars (int32 value, void* destBytes) noexcept; + //============================================================================== /** Turns 4 bytes into a big-endian integer. */ - static uint32 bigEndianInt (const void* bytes) noexcept; + JUCE_CONSTEXPR static uint32 bigEndianInt (const void* bytes) noexcept; /** Turns 8 bytes into a big-endian integer. */ - static uint64 bigEndianInt64 (const void* bytes) noexcept; + JUCE_CONSTEXPR static uint64 bigEndianInt64 (const void* bytes) noexcept; /** Turns 2 bytes into a big-endian integer. */ - static uint16 bigEndianShort (const void* bytes) noexcept; - - //============================================================================== - /** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ - static int littleEndian24Bit (const void* bytes) noexcept; + JUCE_CONSTEXPR static uint16 bigEndianShort (const void* bytes) noexcept; /** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ - static int bigEndian24Bit (const void* bytes) noexcept; - - /** Copies a 24-bit number to 3 little-endian bytes. */ - static void littleEndian24BitToChars (int value, void* destBytes) noexcept; + JUCE_CONSTEXPR static int bigEndian24Bit (const void* bytes) noexcept; /** Copies a 24-bit number to 3 big-endian bytes. */ - static void bigEndian24BitToChars (int value, void* destBytes) noexcept; + static void bigEndian24BitToChars (int32 value, void* destBytes) noexcept; + + //============================================================================== + /** Constructs a 16-bit integer from its constituent bytes, in order of significance. */ + JUCE_CONSTEXPR static uint16 makeInt (uint8 leastSig, uint8 mostSig) noexcept; + + /** Constructs a 32-bit integer from its constituent bytes, in order of significance. */ + JUCE_CONSTEXPR static uint32 makeInt (uint8 leastSig, uint8 byte1, uint8 byte2, uint8 mostSig) noexcept; + + /** Constructs a 64-bit integer from its constituent bytes, in order of significance. */ + JUCE_CONSTEXPR static uint64 makeInt (uint8 leastSig, uint8 byte1, uint8 byte2, uint8 byte3, + uint8 byte4, uint8 byte5, uint8 byte6, uint8 mostSig) noexcept; //============================================================================== /** Returns true if the current CPU is big-endian. */ - JUCE_CONSTEXPR static bool isBigEndian() noexcept; + JUCE_CONSTEXPR static bool isBigEndian() noexcept + { + #if JUCE_LITTLE_ENDIAN + return false; + #else + return true; + #endif + } private: ByteOrder() = delete; - - JUCE_DECLARE_NON_COPYABLE (ByteOrder) }; //============================================================================== +JUCE_CONSTEXPR inline uint16 ByteOrder::swap (uint16 v) noexcept { return static_cast ((v << 8) | (v >> 8)); } +JUCE_CONSTEXPR inline int16 ByteOrder::swap (int16 v) noexcept { return static_cast (swap (static_cast (v))); } +inline int32 ByteOrder::swap (int32 v) noexcept { return static_cast (swap (static_cast (v))); } +inline int64 ByteOrder::swap (int64 v) noexcept { return static_cast (swap (static_cast (v))); } +inline float ByteOrder::swap (float v) noexcept { union { uint32 asUInt; float asFloat; } n; n.asFloat = v; n.asUInt = swap (n.asUInt); return n.asFloat; } +inline double ByteOrder::swap (double v) noexcept { union { uint64 asUInt; double asFloat; } n; n.asFloat = v; n.asUInt = swap (n.asUInt); return n.asFloat; } + #if JUCE_MSVC && ! defined (__INTEL_COMPILER) #pragma intrinsic (_byteswap_ulong) #endif -JUCE_CONSTEXPR inline uint16 ByteOrder::swap (uint16 n) noexcept -{ - return static_cast ((n << 8) | (n >> 8)); -} - inline uint32 ByteOrder::swap (uint32 n) noexcept { #if JUCE_MAC || JUCE_IOS @@ -174,66 +178,43 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept #endif } -#if JUCE_LITTLE_ENDIAN - JUCE_CONSTEXPR inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return v; } - inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return v; } - inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return v; } - JUCE_CONSTEXPR inline int16 ByteOrder::swapIfBigEndian (const int16 v) noexcept { return v; } - inline int32 ByteOrder::swapIfBigEndian (const int32 v) noexcept { return v; } - inline int64 ByteOrder::swapIfBigEndian (const int64 v) noexcept { return v; } - inline float ByteOrder::swapIfBigEndian (const float v) noexcept { return v; } - inline double ByteOrder::swapIfBigEndian (const double v) noexcept { return v; } - - JUCE_CONSTEXPR inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return swap (v); } - inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return swap (v); } - inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return swap (v); } - JUCE_CONSTEXPR inline int16 ByteOrder::swapIfLittleEndian (const int16 v) noexcept { return static_cast (swap (static_cast (v))); } - inline int32 ByteOrder::swapIfLittleEndian (const int32 v) noexcept { return static_cast (swap (static_cast (v))); } - inline int64 ByteOrder::swapIfLittleEndian (const int64 v) noexcept { return static_cast (swap (static_cast (v))); } - inline float ByteOrder::swapIfLittleEndian (const float v) noexcept { union { uint32 asUInt; float asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; } - inline double ByteOrder::swapIfLittleEndian (const double v) noexcept { union { uint64 asUInt; double asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; } - - inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return *static_cast (bytes); } - JUCE_CONSTEXPR inline uint32 ByteOrder::littleEndianInt (char c1, char c2, char c3, char c4) noexcept { return (((uint32) c4) << 24) + (((uint32) c3) << 16) + (((uint32) c2) << 8) + (uint32) c1; } - - inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return *static_cast (bytes); } - inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return *static_cast (bytes); } - inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } - inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } - inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } - JUCE_CONSTEXPR inline bool ByteOrder::isBigEndian() noexcept { return false; } -#else - JUCE_CONSTEXPR inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return swap (v); } - inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return swap (v); } - inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return swap (v); } - JUCE_CONSTEXPR inline int16 ByteOrder::swapIfBigEndian (const int16 v) noexcept { return static_cast (swap (static_cast (v))); } - inline int32 ByteOrder::swapIfBigEndian (const int32 v) noexcept { return static_cast (swap (static_cast (v))); } - inline int64 ByteOrder::swapIfBigEndian (const int64 v) noexcept { return static_cast (swap (static_cast (v))); } - inline float ByteOrder::swapIfBigEndian (const float v) noexcept { union { uint32 asUInt; float asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; } - inline double ByteOrder::swapIfBigEndian (const double v) noexcept { union { uint64 asUInt; double asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; } - - JUCE_CONSTEXPR inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return v; } - inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return v; } - inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return v; } - JUCE_CONSTEXPR inline int16 ByteOrder::swapIfLittleEndian (const int16 v) noexcept { return v; } - inline int32 ByteOrder::swapIfLittleEndian (const int32 v) noexcept { return v; } - inline int64 ByteOrder::swapIfLittleEndian (const int64 v) noexcept { return v; } - inline float ByteOrder::swapIfLittleEndian (const float v) noexcept { return v; } - inline double ByteOrder::swapIfLittleEndian (const double v) noexcept { return v; } - - inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } - JUCE_CONSTEXPR inline uint32 ByteOrder::littleEndianInt (char c1, char c2, char c3, char c4) noexcept { return (((uint32) c1) << 24) + (((uint32) c2) << 16) + (((uint32) c3) << 8) + (uint32) c4; } - inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } - inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } - inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return *static_cast (bytes); } - inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return *static_cast (bytes); } - inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return *static_cast (bytes); } - JUCE_CONSTEXPR inline bool ByteOrder::isBigEndian() noexcept { return true; } -#endif +JUCE_CONSTEXPR inline uint16 ByteOrder::makeInt (uint8 b0, uint8 b1) noexcept +{ + return static_cast (static_cast (b0) | (static_cast (b1) << 8)); +} + +JUCE_CONSTEXPR inline uint32 ByteOrder::makeInt (uint8 b0, uint8 b1, uint8 b2, uint8 b3) noexcept +{ + return static_cast (b0) | (static_cast (b1) << 8) + | (static_cast (b2) << 16) | (static_cast (b3) << 24); +} + +JUCE_CONSTEXPR inline uint64 ByteOrder::makeInt (uint8 b0, uint8 b1, uint8 b2, uint8 b3, uint8 b4, uint8 b5, uint8 b6, uint8 b7) noexcept +{ + return static_cast (b0) | (static_cast (b1) << 8) | (static_cast (b2) << 16) | (static_cast (b3) << 24) + | (static_cast (b4) << 32) | (static_cast (b5) << 40) | (static_cast (b6) << 48) | (static_cast (b7) << 56); +} -inline int ByteOrder::littleEndian24Bit (const void* const bytes) noexcept { return (int) ((((unsigned int) static_cast (bytes)[2]) << 16) | (((unsigned int) static_cast (bytes)[1]) << 8) | ((unsigned int) static_cast (bytes)[0])); } -inline int ByteOrder::bigEndian24Bit (const void* const bytes) noexcept { return (int) ((((unsigned int) static_cast (bytes)[0]) << 16) | (((unsigned int) static_cast (bytes)[1]) << 8) | ((unsigned int) static_cast (bytes)[2])); } -inline void ByteOrder::littleEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast (destBytes)[0] = (uint8) value; static_cast (destBytes)[1] = (uint8) (value >> 8); static_cast (destBytes)[2] = (uint8) (value >> 16); } -inline void ByteOrder::bigEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast (destBytes)[0] = (uint8) (value >> 16); static_cast (destBytes)[1] = (uint8) (value >> 8); static_cast (destBytes)[2] = (uint8) value; } +JUCE_CONSTEXPR inline uint16 ByteOrder::littleEndianShort (const void* bytes) noexcept { return makeInt (static_cast (bytes)[0], static_cast (bytes)[1]); } +JUCE_CONSTEXPR inline uint32 ByteOrder::littleEndianInt (const void* bytes) noexcept { return makeInt (static_cast (bytes)[0], static_cast (bytes)[1], + static_cast (bytes)[2], static_cast (bytes)[3]); } +JUCE_CONSTEXPR inline uint64 ByteOrder::littleEndianInt64 (const void* bytes) noexcept { return makeInt (static_cast (bytes)[0], static_cast (bytes)[1], + static_cast (bytes)[2], static_cast (bytes)[3], + static_cast (bytes)[4], static_cast (bytes)[5], + static_cast (bytes)[6], static_cast (bytes)[7]); } + +JUCE_CONSTEXPR inline uint16 ByteOrder::bigEndianShort (const void* bytes) noexcept { return makeInt (static_cast (bytes)[1], static_cast (bytes)[0]); } +JUCE_CONSTEXPR inline uint32 ByteOrder::bigEndianInt (const void* bytes) noexcept { return makeInt (static_cast (bytes)[3], static_cast (bytes)[2], + static_cast (bytes)[1], static_cast (bytes)[0]); } +JUCE_CONSTEXPR inline uint64 ByteOrder::bigEndianInt64 (const void* bytes) noexcept { return makeInt (static_cast (bytes)[7], static_cast (bytes)[6], + static_cast (bytes)[5], static_cast (bytes)[4], + static_cast (bytes)[3], static_cast (bytes)[2], + static_cast (bytes)[1], static_cast (bytes)[0]); } + +JUCE_CONSTEXPR inline int32 ByteOrder::littleEndian24Bit (const void* bytes) noexcept { return (int32) ((((uint32) static_cast (bytes)[2]) << 16) | (((uint32) static_cast (bytes)[1]) << 8) | ((uint32) static_cast (bytes)[0])); } +JUCE_CONSTEXPR inline int32 ByteOrder::bigEndian24Bit (const void* bytes) noexcept { return (int32) ((((uint32) static_cast (bytes)[0]) << 16) | (((uint32) static_cast (bytes)[1]) << 8) | ((uint32) static_cast (bytes)[2])); } + +inline void ByteOrder::littleEndian24BitToChars (int32 value, void* destBytes) noexcept { static_cast (destBytes)[0] = (uint8) value; static_cast (destBytes)[1] = (uint8) (value >> 8); static_cast (destBytes)[2] = (uint8) (value >> 16); } +inline void ByteOrder::bigEndian24BitToChars (int32 value, void* destBytes) noexcept { static_cast (destBytes)[0] = (uint8) (value >> 16); static_cast (destBytes)[1] = (uint8) (value >> 8); static_cast (destBytes)[2] = (uint8) value; } } // namespace juce diff --git a/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp b/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp index 3910281868..6415f34676 100644 --- a/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp +++ b/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp @@ -29,8 +29,8 @@ namespace juce namespace PropertyFileConstants { - JUCE_CONSTEXPR static const int magicNumber = (int) ByteOrder::littleEndianInt ('P', 'R', 'O', 'P'); - JUCE_CONSTEXPR static const int magicNumberCompressed = (int) ByteOrder::littleEndianInt ('C', 'P', 'R', 'P'); + JUCE_CONSTEXPR static const int magicNumber = (int) ByteOrder::makeInt ('P', 'R', 'O', 'P'); + JUCE_CONSTEXPR static const int magicNumberCompressed = (int) ByteOrder::makeInt ('C', 'P', 'R', 'P'); JUCE_CONSTEXPR static const char* const fileTag = "PROPERTIES"; JUCE_CONSTEXPR static const char* const valueTag = "VALUE";