From 14dd11372f3e76a7289d929387ef38c06f0fbc8d Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Tue, 5 Jan 2021 22:20:09 +0100 Subject: [PATCH 1/2] Utility functions for little-endian <-> big-endian conversion. --- components/misc/endianness.hpp | 82 ++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 components/misc/endianness.hpp diff --git a/components/misc/endianness.hpp b/components/misc/endianness.hpp new file mode 100644 index 0000000000..1b43e584e5 --- /dev/null +++ b/components/misc/endianness.hpp @@ -0,0 +1,82 @@ +#ifndef COMPONENTS_MISC_ENDIANNESS_H +#define COMPONENTS_MISC_ENDIANNESS_H + +#include + +namespace Misc +{ + + // Two-way conversion little-endian <-> big-endian + template + void swapEndiannessInplace(T& v) + { + static_assert(std::is_arithmetic_v); + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8); + + if constexpr (sizeof(T) == 2) + { + uint16_t& v16 = *reinterpret_cast(&v); + v16 = (v16 >> 8) | (v16 << 8); + } + if constexpr (sizeof(T) == 4) + { + uint32_t& v32 = *reinterpret_cast(&v); + v32 = (v32 >> 24) | ((v32 >> 8) & 0xff00) | ((v32 & 0xff00) << 8) || v32 << 24; + } + if constexpr (sizeof(T) == 8) + { + uint64_t& v64 = *reinterpret_cast(&v); + v64 = (v64 >> 56) | ((v64 & 0x00ff'0000'0000'0000) >> 40) | ((v64 & 0x0000'ff00'0000'0000) >> 24) + | ((v64 & 0x0000'00ff'0000'0000) >> 8) | ((v64 & 0x0000'0000'ff00'0000) << 8) + | ((v64 & 0x0000'0000'00ff'0000) << 24) | ((v64 & 0x0000'0000'0000'ff00) << 40) | (v64 << 56); + } + } + + #ifdef _WIN32 + constexpr bool IS_LITTLE_ENDIAN = true; + constexpr bool IS_BIG_ENDIAN = false; + #else + constexpr bool IS_LITTLE_ENDIAN = __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__; + constexpr bool IS_BIG_ENDIAN = __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; + #endif + + // Usage: swapEndiannessInplaceIf(v) - native to little-endian or back + // swapEndiannessInplaceIf(v) - native to big-endian or back + template + void swapEndiannessInplaceIf(T& v) + { + static_assert(std::is_arithmetic_v); + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8); + if constexpr (C) + swapEndiannessInplace(v); + } + + template + T toLittleEndian(T v) + { + swapEndiannessInplaceIf(v); + return v; + } + template + T fromLittleEndian(T v) + { + swapEndiannessInplaceIf(v); + return v; + } + + template + T toBigEndian(T v) + { + swapEndiannessInplaceIf(v); + return v; + } + template + T fromBigEndian(T v) + { + swapEndiannessInplaceIf(v); + return v; + } + +} + +#endif // COMPONENTS_MISC_ENDIANNESS_H From eaaa2f4a1c4daff1e035208eda662476449a4a1f Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Fri, 8 Jan 2021 23:02:19 +0100 Subject: [PATCH 2/2] Use misc/endianness.hpp in components/nif/nifstream --- components/nif/nifstream.cpp | 2 +- components/nif/nifstream.hpp | 98 +++++++++++++----------------------- 2 files changed, 35 insertions(+), 65 deletions(-) diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index 69f1a905b0..07c9c917ce 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -7,7 +7,7 @@ namespace Nif osg::Quat NIFStream::getQuaternion() { float f[4]; - readLittleEndianBufferOfType<4, float,uint32_t>(inp, (float*)&f); + readLittleEndianBufferOfType<4, float>(inp, (float*)&f); osg::Quat quat; quat.w() = f[0]; quat.x() = f[1]; diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 97075c288c..4d221b8676 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -22,61 +23,30 @@ namespace Nif class NIFFile; /* - readLittleEndianBufferOfType: This template should only be used with non POD data types + readLittleEndianBufferOfType: This template should only be used with arithmetic types */ -template inline void readLittleEndianBufferOfType(Files::IStreamPtr &pIStream, T* dest) +template inline void readLittleEndianBufferOfType(Files::IStreamPtr &pIStream, T* dest) { -#if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86) pIStream->read((char*)dest, numInstances * sizeof(T)); -#else - uint8_t* destByteBuffer = (uint8_t*)dest; - pIStream->read((char*)dest, numInstances * sizeof(T)); - /* - Due to the loop iterations being known at compile time, - this nested loop will most likely be unrolled - For example, for 2 instances of a 4 byte data type, you should get the below result - */ - union { - IntegerT i; - T t; - } u; - for (uint32_t i = 0; i < numInstances; i++) - { - u = { 0 }; - for (uint32_t byte = 0; byte < sizeof(T); byte++) - u.i |= (((IntegerT)destByteBuffer[i * sizeof(T) + byte]) << (byte * 8)); - dest[i] = u.t; - } -#endif + if constexpr (Misc::IS_BIG_ENDIAN) + for (uint32_t i = 0; i < numInstances; i++) + Misc::swapEndiannessInplace(dest[i]); } /* - readLittleEndianDynamicBufferOfType: This template should only be used with non POD data types + readLittleEndianDynamicBufferOfType: This template should only be used with arithmetic types */ -template inline void readLittleEndianDynamicBufferOfType(Files::IStreamPtr &pIStream, T* dest, uint32_t numInstances) +template inline void readLittleEndianDynamicBufferOfType(Files::IStreamPtr &pIStream, T* dest, uint32_t numInstances) { -#if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86) pIStream->read((char*)dest, numInstances * sizeof(T)); -#else - uint8_t* destByteBuffer = (uint8_t*)dest; - pIStream->read((char*)dest, numInstances * sizeof(T)); - union { - IntegerT i; - T t; - } u; - for (uint32_t i = 0; i < numInstances; i++) - { - u.i = 0; - for (uint32_t byte = 0; byte < sizeof(T); byte++) - u.i |= ((IntegerT)destByteBuffer[i * sizeof(T) + byte]) << (byte * 8); - dest[i] = u.t; - } -#endif + if constexpr (Misc::IS_BIG_ENDIAN) + for (uint32_t i = 0; i < numInstances; i++) + Misc::swapEndiannessInplace(dest[i]); } -template type inline readLittleEndianType(Files::IStreamPtr &pIStream) +template type inline readLittleEndianType(Files::IStreamPtr &pIStream) { type val; - readLittleEndianBufferOfType<1,type,IntegerT>(pIStream, (type*)&val); + readLittleEndianBufferOfType<1, type>(pIStream, (type*)&val); return val; } @@ -95,59 +65,59 @@ public: char getChar() { - return readLittleEndianType(inp); + return readLittleEndianType(inp); } short getShort() { - return readLittleEndianType(inp); + return readLittleEndianType(inp); } unsigned short getUShort() { - return readLittleEndianType(inp); + return readLittleEndianType(inp); } int getInt() { - return readLittleEndianType(inp); + return readLittleEndianType(inp); } unsigned int getUInt() { - return readLittleEndianType(inp); + return readLittleEndianType(inp); } float getFloat() { - return readLittleEndianType(inp); + return readLittleEndianType(inp); } osg::Vec2f getVector2() { osg::Vec2f vec; - readLittleEndianBufferOfType<2,float,uint32_t>(inp, (float*)&vec._v[0]); + readLittleEndianBufferOfType<2,float>(inp, (float*)&vec._v[0]); return vec; } osg::Vec3f getVector3() { osg::Vec3f vec; - readLittleEndianBufferOfType<3, float,uint32_t>(inp, (float*)&vec._v[0]); + readLittleEndianBufferOfType<3, float>(inp, (float*)&vec._v[0]); return vec; } osg::Vec4f getVector4() { osg::Vec4f vec; - readLittleEndianBufferOfType<4, float,uint32_t>(inp, (float*)&vec._v[0]); + readLittleEndianBufferOfType<4, float>(inp, (float*)&vec._v[0]); return vec; } Matrix3 getMatrix3() { Matrix3 mat; - readLittleEndianBufferOfType<9, float,uint32_t>(inp, (float*)&mat.mValues); + readLittleEndianBufferOfType<9, float>(inp, (float*)&mat.mValues); return mat; } @@ -181,14 +151,14 @@ public: ///Read in a string of the length specified in the file std::string getSizedString() { - size_t size = readLittleEndianType(inp); + size_t size = readLittleEndianType(inp); return getSizedString(size); } ///Specific to Bethesda headers, uses a byte for length std::string getExportString() { - size_t size = static_cast(readLittleEndianType(inp)); + size_t size = static_cast(readLittleEndianType(inp)); return getSizedString(size); } @@ -203,58 +173,58 @@ public: void getChars(std::vector &vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } void getUChars(std::vector &vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } void getUShorts(std::vector &vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } void getFloats(std::vector &vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } void getInts(std::vector &vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } void getUInts(std::vector &vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } void getVector2s(std::vector &vec, size_t size) { vec.resize(size); /* The packed storage of each Vec2f is 2 floats exactly */ - readLittleEndianDynamicBufferOfType(inp,(float*)vec.data(), size*2); + readLittleEndianDynamicBufferOfType(inp,(float*)vec.data(), size*2); } void getVector3s(std::vector &vec, size_t size) { vec.resize(size); /* The packed storage of each Vec3f is 3 floats exactly */ - readLittleEndianDynamicBufferOfType(inp, (float*)vec.data(), size*3); + readLittleEndianDynamicBufferOfType(inp, (float*)vec.data(), size*3); } void getVector4s(std::vector &vec, size_t size) { vec.resize(size); /* The packed storage of each Vec4f is 4 floats exactly */ - readLittleEndianDynamicBufferOfType(inp, (float*)vec.data(), size*4); + readLittleEndianDynamicBufferOfType(inp, (float*)vec.data(), size*4); } void getQuaternions(std::vector &quat, size_t size)