//========= Copyright � 1996-2006, Valve LLC, All rights reserved. ============ // // Purpose: Low level byte swapping routines. // // $NoKeywords: $ //============================================================================= #ifndef BYTESWAP_H #define BYTESWAP_H #if defined(_WIN32) #pragma once #endif #include "tier0/dbg.h" #include "public/datamap.h" // needed for typedescription_t. note datamap.h is tier1 as well. class CByteSwap { public: CByteSwap() { // Default behavior sets the target endian to match the machine native endian (no swap). SetTargetBigEndian(IsMachineBigEndian()); } //----------------------------------------------------------------------------- // Write a single field. //----------------------------------------------------------------------------- void SwapFieldToTargetEndian(void* pOutputBuffer, void* pData, typedescription_t* pField); //----------------------------------------------------------------------------- // Write a block of fields. Works a bit like the saverestore code. //----------------------------------------------------------------------------- void SwapFieldsToTargetEndian(void* pOutputBuffer, void* pBaseData, datamap_t* pDataMap); // Swaps fields for the templated type to the output buffer. template inline void SwapFieldsToTargetEndian(T* pOutputBuffer, void* pBaseData, unsigned int objectCount = 1) { for (unsigned int i = 0; i < objectCount; ++i, ++pOutputBuffer) { SwapFieldsToTargetEndian((void*)pOutputBuffer, pBaseData, &T::m_DataMap); pBaseData = (byte*)pBaseData + sizeof(T); } } // Swaps fields for the templated type in place. template inline void SwapFieldsToTargetEndian(T* pOutputBuffer, unsigned int objectCount = 1) { SwapFieldsToTargetEndian(pOutputBuffer, (void*)pOutputBuffer, objectCount); } //----------------------------------------------------------------------------- // True if the current machine is detected as big endian. // (Endianness is effectively detected at compile time when optimizations are // enabled) //----------------------------------------------------------------------------- static bool IsMachineBigEndian() { short nIsBigEndian = 1; // if we are big endian, the first byte will be a 0, if little endian, it will be a one. return (bool)(0 == *(char*)&nIsBigEndian); } //----------------------------------------------------------------------------- // Sets the target byte ordering we are swapping to or from. // // Braindead Endian Reference: // x86 is LITTLE Endian // PowerPC is BIG Endian //----------------------------------------------------------------------------- inline void SetTargetBigEndian(bool bigEndian) { m_bBigEndian = bigEndian; m_bSwapBytes = IsMachineBigEndian() != bigEndian; } // Changes target endian inline void FlipTargetEndian(void) { m_bSwapBytes = !m_bSwapBytes; m_bBigEndian = !m_bBigEndian; } // Forces byte swapping state, regardless of endianess inline void ActivateByteSwapping(bool bActivate) { SetTargetBigEndian(IsMachineBigEndian() != bActivate); } //----------------------------------------------------------------------------- // Returns true if the target machine is the same as this one in endianness. // // Used to determine when a byteswap needs to take place. //----------------------------------------------------------------------------- inline bool IsSwappingBytes(void) // Are bytes being swapped? { return m_bSwapBytes; } inline bool IsTargetBigEndian(void) // What is the current target endian? { return m_bBigEndian; } //----------------------------------------------------------------------------- // IsByteSwapped() // // When supplied with a chunk of input data and a constant or magic number // (in native format) determines the endienness of the current machine in // relation to the given input data. // // Returns: // 1 if input is the same as nativeConstant. // 0 if input is byteswapped relative to nativeConstant. // -1 if input is not the same as nativeConstant and not byteswapped either. // // ( This is useful for detecting byteswapping in magic numbers in structure // headers for example. ) //----------------------------------------------------------------------------- template inline int SourceIsNativeEndian(T input, T nativeConstant) { // If it's the same, it isn't byteswapped: if (input == nativeConstant) return 1; int output; LowLevelByteSwap(&output, &input); if (output == nativeConstant) return 0; Assert(0); // if we get here, input is neither a swapped nor unswapped version of nativeConstant. return -1; } //----------------------------------------------------------------------------- // Swaps an input buffer full of type T into the given output buffer. // // Swaps [count] items from the inputBuffer to the outputBuffer. // If inputBuffer is omitted or NULL, then it is assumed to be the same as // outputBuffer - effectively swapping the contents of the buffer in place. //----------------------------------------------------------------------------- template inline void SwapBuffer(T* outputBuffer, T* inputBuffer = NULL, int count = 1) { Assert(count >= 0); Assert(outputBuffer); // Fail gracefully in release: if (count <= 0 || !outputBuffer) return; // Optimization for the case when we are swapping in place. if (inputBuffer == NULL) { inputBuffer = outputBuffer; } // Swap everything in the buffer: for (int i = 0; i < count; i++) { LowLevelByteSwap(&outputBuffer[i], &inputBuffer[i]); } } //----------------------------------------------------------------------------- // Swaps an input buffer full of type T into the given output buffer. // // Swaps [count] items from the inputBuffer to the outputBuffer. // If inputBuffer is omitted or NULL, then it is assumed to be the same as // outputBuffer - effectively swapping the contents of the buffer in place. //----------------------------------------------------------------------------- template inline void SwapBufferToTargetEndian(T* outputBuffer, T* inputBuffer = NULL, int count = 1) { Assert(count >= 0); Assert(outputBuffer); // Fail gracefully in release: if (count <= 0 || !outputBuffer) return; // Optimization for the case when we are swapping in place. if (inputBuffer == NULL) { inputBuffer = outputBuffer; } // Are we already the correct endienness? ( or are we swapping 1 byte items? ) if (!m_bSwapBytes || (sizeof(T) == 1)) { // If we were just going to swap in place then return. if (!inputBuffer) return; // Otherwise copy the inputBuffer to the outputBuffer: if (outputBuffer != inputBuffer) memcpy(outputBuffer, inputBuffer, count * sizeof(T)); return; } // Swap everything in the buffer: for (int i = 0; i < count; i++) { LowLevelByteSwap(&outputBuffer[i], &inputBuffer[i]); } } private: //----------------------------------------------------------------------------- // The lowest level byte swapping workhorse of doom. output always contains the // swapped version of input. ( Doesn't compare machine to target endianness ) //----------------------------------------------------------------------------- template static void LowLevelByteSwap(T* output, T* input) { T temp = *output; #if defined( _X360 ) // Intrinsics need the source type to be fixed-point DWORD* word = (DWORD*)input; switch (sizeof(T)) { case 8: { __storewordbytereverse(*(word + 1), 0, &temp); __storewordbytereverse(*(word + 0), 4, &temp); } break; case 4: __storewordbytereverse(*word, 0, &temp); break; case 2: __storeshortbytereverse(*input, 0, &temp); break; case 1: Q_memcpy(&temp, input, 1); break; default: Assert("Invalid size in CByteswap::LowLevelByteSwap" && 0); } #else for (unsigned int i = 0; i < sizeof(T); i++) { ((unsigned char*)&temp)[i] = ((unsigned char*)input)[sizeof(T) - (i + 1)]; } #endif memcpy(output, &temp, sizeof(T)); } #if defined( _X360 ) // specialized for void * to get 360 XDK compile working despite changelist 281331 //----------------------------------------------------------------------------- // The lowest level byte swapping workhorse of doom. output always contains the // swapped version of input. ( Doesn't compare machine to target endianness ) //----------------------------------------------------------------------------- template<> static void LowLevelByteSwap(void** output, void** input) { AssertMsgOnce(sizeof(void*) == sizeof(unsigned int), "void *'s on this platform are not four bytes!"); __storewordbytereverse(*reinterpret_cast(input), 0, output); } #endif unsigned int m_bSwapBytes : 1; unsigned int m_bBigEndian : 1; }; #endif /* !BYTESWAP_H */