|
|
//========= 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<typename T> 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<typename T> inline void SwapFieldsToTargetEndian( T* pOutputBuffer, unsigned int objectCount = 1 ) { SwapFieldsToTargetEndian<T>( 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<typename T> inline int SourceIsNativeEndian( T input, T nativeConstant ) { // If it's the same, it isn't byteswapped:
if( input == nativeConstant ) return 1;
int output; LowLevelByteSwap<T>( &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<typename T> 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<T>( &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<typename T> 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<T>( &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<typename T> 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 */
|