/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #ifndef __BITMSG_H__ #define __BITMSG_H__ /* ================================================ idBitMsg operates on a sequence of individual bits. It handles byte ordering and avoids alignment errors. It allows concurrent writing and reading. The data set with Init is never free-d. ================================================ */ class idBitMsg { public: idBitMsg() { InitWrite( NULL, 0 ); } idBitMsg( byte * data, int length ) { InitWrite( data, length ); } idBitMsg( const byte * data, int length ) { InitRead( data, length ); } // both read & write void InitWrite( byte *data, int length ); // read only void InitRead( const byte *data, int length ); // get data for writing byte * GetWriteData(); // get data for reading const byte * GetReadData() const; // get the maximum message size int GetMaxSize() const; // generate error if not set and message is overflowed void SetAllowOverflow( bool set ); // returns true if the message was overflowed bool IsOverflowed() const; // size of the message in bytes int GetSize() const; // set the message size void SetSize( int size ); // get current write bit int GetWriteBit() const; // set current write bit void SetWriteBit( int bit ); // returns number of bits written int GetNumBitsWritten() const; // space left in bytes for writing int GetRemainingSpace() const; // space left in bits for writing int GetRemainingWriteBits() const; //------------------------ // Write State //------------------------ // save the write state void SaveWriteState( int &s, int &b, uint64 &t ) const; // restore the write state void RestoreWriteState( int s, int b, uint64 t ); //------------------------ // Reading //------------------------ // bytes read so far int GetReadCount() const; // set the number of bytes and bits read void SetReadCount( int bytes ); // get current read bit int GetReadBit() const; // set current read bit void SetReadBit( int bit ); // returns number of bits read int GetNumBitsRead() const; // number of bytes left to read int GetRemainingData() const; // number of bits left to read int GetRemainingReadBits() const; // save the read state void SaveReadState( int &c, int &b ) const; // restore the read state void RestoreReadState( int c, int b ); //------------------------ // Writing //------------------------ // begin writing void BeginWriting(); // write up to the next byte boundary void WriteByteAlign(); // write the specified number of bits void WriteBits( int value, int numBits ); void WriteBool( bool c ); void WriteChar( int8 c ); void WriteByte( uint8 c ); void WriteShort( int16 c ); void WriteUShort( uint16 c ); void WriteLong( int32 c ); void WriteLongLong( int64 c ); void WriteFloat( float f ); void WriteFloat( float f, int exponentBits, int mantissaBits ); void WriteAngle8( float f ); void WriteAngle16( float f ); void WriteDir( const idVec3 &dir, int numBits ); void WriteString( const char *s, int maxLength = -1, bool make7Bit = true ); void WriteData( const void *data, int length ); void WriteNetadr( const netadr_t adr ); void WriteUNorm8( float f ) { WriteByte( idMath::Ftob( f * 255.0f ) ); } void WriteUNorm16( float f ) { WriteUShort( idMath::Ftoi( f * 65535.0f ) ); } void WriteNorm16( float f ) { WriteShort( idMath::Ftoi( f * 32767.0f ) ); } void WriteDeltaChar( int8 oldValue, int8 newValue ) { WriteByte( newValue - oldValue ); } void WriteDeltaByte( uint8 oldValue, uint8 newValue ) { WriteByte( newValue - oldValue ); } void WriteDeltaShort( int16 oldValue, int16 newValue ) { WriteUShort( newValue - oldValue ); } void WriteDeltaUShort( uint16 oldValue, uint16 newValue ) { WriteUShort( newValue - oldValue ); } void WriteDeltaLong( int32 oldValue, int32 newValue ) { WriteLong( newValue - oldValue ); } void WriteDeltaFloat( float oldValue, float newValue ) { WriteFloat( newValue - oldValue ); } void WriteDeltaFloat( float oldValue, float newValue, int exponentBits, int mantissaBits ) { WriteFloat( newValue - oldValue, exponentBits, mantissaBits ); } bool WriteDeltaDict( const idDict &dict, const idDict *base ); template< int _max_, int _numBits_ > void WriteQuantizedFloat( float value ); template< int _max_, int _numBits_ > void WriteQuantizedUFloat( float value ); // Quantize a float to a variable number of bits (assumes unsigned, uses simple quantization) template< typename T > void WriteVectorFloat( const T & v ) { for ( int i = 0; i < v.GetDimension(); i++ ) { WriteFloat( v[i] ); } } template< typename T > void WriteVectorUNorm8( const T & v ) { for ( int i = 0; i < v.GetDimension(); i++ ) { WriteUNorm8( v[i] ); } } template< typename T > void WriteVectorUNorm16( const T & v ) { for ( int i = 0; i < v.GetDimension(); i++ ) { WriteUNorm16( v[i] ); } } template< typename T > void WriteVectorNorm16( const T & v ) { for ( int i = 0; i < v.GetDimension(); i++ ) { WriteNorm16( v[i] ); } } // Compress a vector to a variable number of bits (assumes signed, uses simple quantization) template< typename T, int _max_, int _numBits_ > void WriteQuantizedVector( const T & v ) { for ( int i = 0; i < v.GetDimension(); i++ ) { WriteQuantizedFloat< _max_, _numBits_ >( v[i] ); } } // begin reading. void BeginReading() const; // read up to the next byte boundary void ReadByteAlign() const; // read the specified number of bits int ReadBits( int numBits ) const; bool ReadBool() const; int ReadChar() const; int ReadByte() const; int ReadShort() const; int ReadUShort() const; int ReadLong() const; int64 ReadLongLong() const; float ReadFloat() const; float ReadFloat( int exponentBits, int mantissaBits ) const; float ReadAngle8() const; float ReadAngle16() const; idVec3 ReadDir( int numBits ) const; int ReadString( char *buffer, int bufferSize ) const; int ReadString( idStr & str ) const; int ReadData( void *data, int length ) const; void ReadNetadr( netadr_t *adr ) const; float ReadUNorm8() const { return ReadByte() / 255.0f; } float ReadUNorm16() const { return ReadUShort() / 65535.0f; } float ReadNorm16() const { return ReadShort() / 32767.0f; } int8 ReadDeltaChar( int8 oldValue ) const { return oldValue + ReadByte(); } uint8 ReadDeltaByte( uint8 oldValue ) const { return oldValue + ReadByte(); } int16 ReadDeltaShort( int16 oldValue ) const { return oldValue + ReadUShort(); } uint16 ReadDeltaUShort( uint16 oldValue ) const { return oldValue + ReadUShort(); } int32 ReadDeltaLong( int32 oldValue ) const { return oldValue + ReadLong(); } float ReadDeltaFloat( float oldValue ) const { return oldValue + ReadFloat(); } float ReadDeltaFloat( float oldValue, int exponentBits, int mantissaBits ) const { return oldValue + ReadFloat( exponentBits, mantissaBits ); } bool ReadDeltaDict( idDict &dict, const idDict *base ) const; template< int _max_, int _numBits_ > float ReadQuantizedFloat() const; template< int _max_, int _numBits_ > float ReadQuantizedUFloat() const; template< typename T > void ReadVectorFloat( T & v ) const { for ( int i = 0; i < v.GetDimension(); i++ ) { v[i] = ReadFloat(); } } template< typename T > void ReadVectorUNorm8( T & v ) const { for ( int i = 0; i < v.GetDimension(); i++ ) { v[i] = ReadUNorm8(); } } template< typename T > void ReadVectorUNorm16( T & v ) const { for ( int i = 0; i < v.GetDimension(); i++ ) { v[i] = ReadUNorm16(); } } template< typename T > void ReadVectorNorm16( T & v ) const { for ( int i = 0; i < v.GetDimension(); i++ ) { v[i] = ReadNorm16(); } } template< typename T, int _max_, int _numBits_ > void ReadQuantizedVector( T & v ) const { for ( int i = 0; i < v.GetDimension(); i++ ) { v[i] = ReadQuantizedFloat< _max_, _numBits_ >(); } } static int DirToBits( const idVec3 &dir, int numBits ); static idVec3 BitsToDir( int bits, int numBits ); void SetHasChanged( bool b ) { hasChanged = b; } bool HasChanged() const { return hasChanged; } private: byte * writeData; // pointer to data for writing const byte * readData; // pointer to data for reading int maxSize; // maximum size of message in bytes int curSize; // current size of message in bytes mutable int writeBit; // number of bits written to the last written byte mutable int readCount; // number of bytes read so far mutable int readBit; // number of bits read from the last read byte bool allowOverflow; // if false, generate error when the message is overflowed bool overflowed; // set true if buffer size failed (with allowOverflow set) bool hasChanged; // Hack mutable uint64 tempValue; private: bool CheckOverflow( int numBits ); byte * GetByteSpace( int length ); }; /* ======================== idBitMsg::InitWrite ======================== */ ID_INLINE void idBitMsg::InitWrite( byte *data, int length ) { writeData = data; readData = data; maxSize = length; curSize = 0; writeBit = 0; readCount = 0; readBit = 0; allowOverflow = false; overflowed = false; tempValue = 0; } /* ======================== idBitMsg::InitRead ======================== */ ID_INLINE void idBitMsg::InitRead( const byte *data, int length ) { writeData = NULL; readData = data; maxSize = length; curSize = length; writeBit = 0; readCount = 0; readBit = 0; allowOverflow = false; overflowed = false; tempValue = 0; } /* ======================== idBitMsg::GetWriteData ======================== */ ID_INLINE byte *idBitMsg::GetWriteData() { return writeData; } /* ======================== idBitMsg::GetReadData ======================== */ ID_INLINE const byte *idBitMsg::GetReadData() const { return readData; } /* ======================== idBitMsg::GetMaxSize ======================== */ ID_INLINE int idBitMsg::GetMaxSize() const { return maxSize; } /* ======================== idBitMsg::SetAllowOverflow ======================== */ ID_INLINE void idBitMsg::SetAllowOverflow( bool set ) { allowOverflow = set; } /* ======================== idBitMsg::IsOverflowed ======================== */ ID_INLINE bool idBitMsg::IsOverflowed() const { return overflowed; } /* ======================== idBitMsg::GetSize ======================== */ ID_INLINE int idBitMsg::GetSize() const { return curSize + ( writeBit != 0 ); } /* ======================== idBitMsg::SetSize ======================== */ ID_INLINE void idBitMsg::SetSize( int size ) { assert( writeBit == 0 ); if ( size > maxSize ) { curSize = maxSize; } else { curSize = size; } } /* ======================== idBitMsg::GetWriteBit ======================== */ ID_INLINE int idBitMsg::GetWriteBit() const { return writeBit; } /* ======================== idBitMsg::SetWriteBit ======================== */ ID_INLINE void idBitMsg::SetWriteBit( int bit ) { // see idBitMsg::WriteByteAlign assert( false ); writeBit = bit & 7; if ( writeBit ) { writeData[curSize - 1] &= ( 1 << writeBit ) - 1; } } /* ======================== idBitMsg::GetNumBitsWritten ======================== */ ID_INLINE int idBitMsg::GetNumBitsWritten() const { return ( curSize << 3 ) + writeBit; } /* ======================== idBitMsg::GetRemainingSpace ======================== */ ID_INLINE int idBitMsg::GetRemainingSpace() const { return maxSize - GetSize(); } /* ======================== idBitMsg::GetRemainingWriteBits ======================== */ ID_INLINE int idBitMsg::GetRemainingWriteBits() const { return ( maxSize << 3 ) - GetNumBitsWritten(); } /* ======================== idBitMsg::SaveWriteState ======================== */ ID_INLINE void idBitMsg::SaveWriteState( int &s, int &b, uint64 &t ) const { s = curSize; b = writeBit; t = tempValue; } /* ======================== idBitMsg::RestoreWriteState ======================== */ ID_INLINE void idBitMsg::RestoreWriteState( int s, int b, uint64 t ) { curSize = s; writeBit = b & 7; if ( writeBit ) { writeData[curSize] &= ( 1 << writeBit ) - 1; } tempValue = t; } /* ======================== idBitMsg::GetReadCount ======================== */ ID_INLINE int idBitMsg::GetReadCount() const { return readCount; } /* ======================== idBitMsg::SetReadCount ======================== */ ID_INLINE void idBitMsg::SetReadCount( int bytes ) { readCount = bytes; } /* ======================== idBitMsg::GetReadBit ======================== */ ID_INLINE int idBitMsg::GetReadBit() const { return readBit; } /* ======================== idBitMsg::SetReadBit ======================== */ ID_INLINE void idBitMsg::SetReadBit( int bit ) { readBit = bit & 7; } /* ======================== idBitMsg::GetNumBitsRead ======================== */ ID_INLINE int idBitMsg::GetNumBitsRead() const { return ( ( readCount << 3 ) - ( ( 8 - readBit ) & 7 ) ); } /* ======================== idBitMsg::GetRemainingData ======================== */ ID_INLINE int idBitMsg::GetRemainingData() const { assert( writeBit == 0 ); return curSize - readCount; } /* ======================== idBitMsg::GetRemainingReadBits ======================== */ ID_INLINE int idBitMsg::GetRemainingReadBits() const { assert( writeBit == 0 ); return ( curSize << 3 ) - GetNumBitsRead(); } /* ======================== idBitMsg::SaveReadState ======================== */ ID_INLINE void idBitMsg::SaveReadState( int &c, int &b ) const { assert( writeBit == 0 ); c = readCount; b = readBit; } /* ======================== idBitMsg::RestoreReadState ======================== */ ID_INLINE void idBitMsg::RestoreReadState( int c, int b ) { assert( writeBit == 0 ); readCount = c; readBit = b & 7; } /* ======================== idBitMsg::BeginWriting ======================== */ ID_INLINE void idBitMsg::BeginWriting() { curSize = 0; overflowed = false; writeBit = 0; tempValue = 0; } /* ======================== idBitMsg::WriteByteAlign ======================== */ ID_INLINE void idBitMsg::WriteByteAlign() { // it is important that no uninitialized data slips in the msg stream, // because we use memcmp to decide if entities have changed and wether we should transmit them // this function has the potential to leave uninitialized bits into the stream, // however idBitMsg::WriteBits is properly initializing the byte to 0 so hopefully we are still safe // adding this extra check just in case curSize += writeBit != 0; assert( writeBit == 0 || ( ( writeData[curSize - 1] >> writeBit ) == 0 ) ); // had to early out writeBit == 0 because when writeBit == 0 writeData[curSize - 1] may be the previous byte written and trigger false positives writeBit = 0; tempValue = 0; } /* ======================== idBitMsg::WriteBool ======================== */ ID_INLINE void idBitMsg::WriteBool( bool c ) { WriteBits( c, 1 ); } /* ======================== idBitMsg::WriteChar ======================== */ ID_INLINE void idBitMsg::WriteChar( int8 c ) { WriteBits( c, -8 ); } /* ======================== idBitMsg::WriteByte ======================== */ ID_INLINE void idBitMsg::WriteByte( uint8 c ) { WriteBits( c, 8 ); } /* ======================== idBitMsg::WriteShort ======================== */ ID_INLINE void idBitMsg::WriteShort( int16 c ) { WriteBits( c, -16 ); } /* ======================== idBitMsg::WriteUShort ======================== */ ID_INLINE void idBitMsg::WriteUShort( uint16 c ) { WriteBits( c, 16 ); } /* ======================== idBitMsg::WriteLong ======================== */ ID_INLINE void idBitMsg::WriteLong( int32 c ) { WriteBits( c, 32 ); } /* ======================== idBitMsg::WriteLongLong ======================== */ ID_INLINE void idBitMsg::WriteLongLong( int64 c ) { int a = c; int b = c >> 32; WriteBits( a, 32 ); WriteBits( b, 32 ); } /* ======================== idBitMsg::WriteFloat ======================== */ ID_INLINE void idBitMsg::WriteFloat( float f ) { WriteBits( *reinterpret_cast(&f), 32 ); } /* ======================== idBitMsg::WriteFloat ======================== */ ID_INLINE void idBitMsg::WriteFloat( float f, int exponentBits, int mantissaBits ) { int bits = idMath::FloatToBits( f, exponentBits, mantissaBits ); WriteBits( bits, 1 + exponentBits + mantissaBits ); } /* ======================== idBitMsg::WriteAngle8 ======================== */ ID_INLINE void idBitMsg::WriteAngle8( float f ) { WriteByte( ANGLE2BYTE( f ) ); } /* ======================== idBitMsg::WriteAngle16 ======================== */ ID_INLINE void idBitMsg::WriteAngle16( float f ) { WriteShort( ANGLE2SHORT(f) ); } /* ======================== idBitMsg::WriteDir ======================== */ ID_INLINE void idBitMsg::WriteDir( const idVec3 &dir, int numBits ) { WriteBits( DirToBits( dir, numBits ), numBits ); } /* ======================== idBitMsg::BeginReading ======================== */ ID_INLINE void idBitMsg::BeginReading() const { readCount = 0; readBit = 0; writeBit = 0; tempValue = 0; } /* ======================== idBitMsg::ReadByteAlign ======================== */ ID_INLINE void idBitMsg::ReadByteAlign() const { readBit = 0; } /* ======================== idBitMsg::ReadBool ======================== */ ID_INLINE bool idBitMsg::ReadBool() const { return ( ReadBits( 1 ) == 1 ) ? true : false; } /* ======================== idBitMsg::ReadChar ======================== */ ID_INLINE int idBitMsg::ReadChar() const { return (signed char)ReadBits( -8 ); } /* ======================== idBitMsg::ReadByte ======================== */ ID_INLINE int idBitMsg::ReadByte() const { return (unsigned char)ReadBits( 8 ); } /* ======================== idBitMsg::ReadShort ======================== */ ID_INLINE int idBitMsg::ReadShort() const { return (short)ReadBits( -16 ); } /* ======================== idBitMsg::ReadUShort ======================== */ ID_INLINE int idBitMsg::ReadUShort() const { return (unsigned short)ReadBits( 16 ); } /* ======================== idBitMsg::ReadLong ======================== */ ID_INLINE int idBitMsg::ReadLong() const { return ReadBits( 32 ); } /* ======================== idBitMsg::ReadLongLong ======================== */ ID_INLINE int64 idBitMsg::ReadLongLong() const { int64 a = ReadBits( 32 ); int64 b = ReadBits( 32 ); int64 c = ( 0x00000000ffffffff & a ) | ( b << 32 ); return c; } /* ======================== idBitMsg::ReadFloat ======================== */ ID_INLINE float idBitMsg::ReadFloat() const { float value; *reinterpret_cast(&value) = ReadBits( 32 ); return value; } /* ======================== idBitMsg::ReadFloat ======================== */ ID_INLINE float idBitMsg::ReadFloat( int exponentBits, int mantissaBits ) const { int bits = ReadBits( 1 + exponentBits + mantissaBits ); return idMath::BitsToFloat( bits, exponentBits, mantissaBits ); } /* ======================== idBitMsg::ReadAngle8 ======================== */ ID_INLINE float idBitMsg::ReadAngle8() const { return BYTE2ANGLE( ReadByte() ); } /* ======================== idBitMsg::ReadAngle16 ======================== */ ID_INLINE float idBitMsg::ReadAngle16() const { return SHORT2ANGLE( ReadShort() ); } /* ======================== idBitMsg::ReadDir ======================== */ ID_INLINE idVec3 idBitMsg::ReadDir( int numBits ) const { return BitsToDir( ReadBits( numBits ), numBits ); } /* ======================== idBitMsg::WriteQuantizedFloat ======================== */ template< int _max_, int _numBits_ > ID_INLINE void idBitMsg::WriteQuantizedFloat( float value ) { enum { storeMax = ( 1 << ( _numBits_ - 1 ) ) - 1 }; if ( _max_ > storeMax ) { // Scaling down (scale should be < 1) const float scale = (float)storeMax / (float)_max_; WriteBits( idMath::ClampInt( -storeMax, storeMax, idMath::Ftoi( value * scale ) ), -_numBits_ ); } else { // Scaling up (scale should be >= 1) (Preserve whole numbers when possible) enum { scale = storeMax / _max_ }; WriteBits( idMath::ClampInt( -storeMax, storeMax, idMath::Ftoi( value * scale ) ), -_numBits_ ); } } /* ======================== idBitMsg::WriteQuantizedUFloat ======================== */ template< int _max_, int _numBits_ > ID_INLINE void idBitMsg::WriteQuantizedUFloat( float value ) { enum { storeMax = ( 1 << _numBits_ ) - 1 }; if ( _max_ > storeMax ) { // Scaling down (scale should be < 1) const float scale = (float)storeMax / (float)_max_; WriteBits( idMath::ClampInt( 0, storeMax, idMath::Ftoi( value * scale ) ), _numBits_ ); } else { // Scaling up (scale should be >= 1) (Preserve whole numbers when possible) enum { scale = storeMax / _max_ }; WriteBits( idMath::ClampInt( 0, storeMax, idMath::Ftoi( value * scale ) ), _numBits_ ); } } /* ======================== idBitMsg::ReadQuantizedFloat ======================== */ template< int _max_, int _numBits_ > ID_INLINE float idBitMsg::ReadQuantizedFloat() const { enum { storeMax = ( 1 << ( _numBits_ - 1 ) ) - 1 }; if ( _max_ > storeMax ) { // Scaling down (scale should be < 1) const float invScale = (float)_max_ / (float)storeMax; return (float)ReadBits( -_numBits_ ) * invScale; } else { // Scaling up (scale should be >= 1) (Preserve whole numbers when possible) // Scale will be a whole number. // We use a float to get rid of (potential divide by zero) which is handled above, but the compiler is dumb const float scale = storeMax / _max_; const float invScale = 1.0f / scale; return (float)ReadBits( -_numBits_ ) * invScale; } } /* ======================== idBitMsg::ReadQuantizedUFloat ======================== */ template< int _max_, int _numBits_ > float idBitMsg::ReadQuantizedUFloat() const { enum { storeMax = ( 1 << _numBits_ ) - 1 }; if ( _max_ > storeMax ) { // Scaling down (scale should be < 1) const float invScale = (float)_max_ / (float)storeMax; return (float)ReadBits( _numBits_ ) * invScale; } else { // Scaling up (scale should be >= 1) (Preserve whole numbers when possible) // Scale will be a whole number. // We use a float to get rid of (potential divide by zero) which is handled above, but the compiler is dumb const float scale = storeMax / _max_; const float invScale = 1.0f / scale; return (float)ReadBits( _numBits_ ) * invScale; } } /* ================ WriteFloatArray Writes all the values from the array to the bit message. ================ */ template< class _arrayType_ > void WriteFloatArray( idBitMsg & message, const _arrayType_ & sourceArray ) { for( int i = 0; i < idTupleSize< _arrayType_ >::value; ++i ) { message.WriteFloat( sourceArray[i] ); } } /* ================ WriteFloatArrayDelta Writes _num_ values from the array to the bit message. ================ */ template< class _arrayType_ > void WriteDeltaFloatArray( idBitMsg & message, const _arrayType_ & oldArray, const _arrayType_ & newArray ) { for( int i = 0; i < idTupleSize< _arrayType_ >::value; ++i ) { message.WriteDeltaFloat( oldArray[i], newArray[i] ); } } /* ================ ReadFloatArray Reads _num_ values from the array to the bit message. ================ */ template< class _arrayType_ > _arrayType_ ReadFloatArray( const idBitMsg & message ) { _arrayType_ result; for( int i = 0; i < idTupleSize< _arrayType_ >::value; ++i ) { result[i] = message.ReadFloat(); } return result; } /* ================ ReadDeltaFloatArray Reads _num_ values from the array to the bit message. ================ */ template< class _arrayType_ > _arrayType_ ReadDeltaFloatArray( const idBitMsg & message, const _arrayType_ & oldArray ) { _arrayType_ result; for( int i = 0; i < idTupleSize< _arrayType_ >::value; ++i ) { result[i] = message.ReadDeltaFloat( oldArray[i] ); } return result; } #endif /* !__BITMSG_H__ */