//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Low level byte swapping routines. // // $NoKeywords: $ //============================================================================= #ifndef BYTESWAP_H #define BYTESWAP_H #if defined(_WIN32) #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( size_t 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 */