//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Low level byte swapping routines. // // $NoKeywords: $ //============================================================================= #ifndef BYTESWAP_H #define BYTESWAP_H #if defined(_WIN32) && !defined(__GNUG__) #pragma once #endif #include "../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. // (Endienness 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: 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, 0, &temp); __storewordbytereverse(*(word + 1), 4, &temp); } break; case 4: __storewordbytereverse(*word, 0, &temp); break; case 2: __storeshortbytereverse(*input, 0, &temp); break; default: Assert("Invalid size in CByteswap::LowLevelByteSwap" && 0); } #else for (int i = 0; i < sizeof(T); i++) { ((unsigned char *)&temp)[i] = ((unsigned char *)input)[sizeof(T) - (i + 1)]; } #endif Q_memcpy(output, &temp, sizeof(T)); } unsigned int m_bSwapBytes : 1; unsigned int m_bBigEndian : 1; }; #endif /* !BYTESWAP_H */