You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1293 lines
28 KiB
1293 lines
28 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "bitbuf.h"
|
|
#include "coordsize.h"
|
|
#include "mathlib/vector.h"
|
|
#include "mathlib/mathlib.h"
|
|
#include "tier1/strtools.h"
|
|
#include "bitvec.h"
|
|
#include "vstdlib/random.h"
|
|
|
|
// FIXME: Can't use this until we get multithreaded allocations in tier0 working for tools
|
|
// This is used by VVIS and fails to link
|
|
// NOTE: This must be the last file included!!!
|
|
//#include "tier0/memdbgon.h"
|
|
|
|
#ifdef _X360
|
|
// mandatory ... wary of above comment and isolating, tier0 is built as MT though
|
|
#include "tier0/memdbgon.h"
|
|
#endif
|
|
|
|
#include "stdio.h"
|
|
|
|
#ifndef NDEBUG
|
|
static volatile char const *pDebugString;
|
|
#define DEBUG_LINK_CHECK pDebugString = "tier1.lib built debug!"
|
|
#else
|
|
#define DEBUG_LINK_CHECK
|
|
#endif
|
|
|
|
void CBitWrite::StartWriting( void *pData, int nBytes, int iStartBit, int nBits )
|
|
{
|
|
// Make sure it's dword aligned and padded.
|
|
DEBUG_LINK_CHECK;
|
|
Assert( (nBytes % 4) == 0 );
|
|
Assert(((uintp)pData & 3) == 0);
|
|
Assert( iStartBit == 0 );
|
|
m_pData = (uint32 *) pData;
|
|
m_pDataOut = m_pData;
|
|
m_nDataBytes = nBytes;
|
|
|
|
if ( nBits == -1 )
|
|
{
|
|
m_nDataBits = nBytes << 3;
|
|
}
|
|
else
|
|
{
|
|
Assert( nBits <= nBytes*8 );
|
|
m_nDataBits = nBits;
|
|
}
|
|
m_bOverflow = false;
|
|
m_nOutBufWord = 0;
|
|
m_nOutBitsAvail = 32;
|
|
m_pBufferEnd = m_pDataOut + ( nBytes >> 2 );
|
|
}
|
|
|
|
const uint32 CBitBuffer::s_nMaskTable[33] = {
|
|
0,
|
|
( 1 << 1 ) - 1,
|
|
( 1 << 2 ) - 1,
|
|
( 1 << 3 ) - 1,
|
|
( 1 << 4 ) - 1,
|
|
( 1 << 5 ) - 1,
|
|
( 1 << 6 ) - 1,
|
|
( 1 << 7 ) - 1,
|
|
( 1 << 8 ) - 1,
|
|
( 1 << 9 ) - 1,
|
|
( 1 << 10 ) - 1,
|
|
( 1 << 11 ) - 1,
|
|
( 1 << 12 ) - 1,
|
|
( 1 << 13 ) - 1,
|
|
( 1 << 14 ) - 1,
|
|
( 1 << 15 ) - 1,
|
|
( 1 << 16 ) - 1,
|
|
( 1 << 17 ) - 1,
|
|
( 1 << 18 ) - 1,
|
|
( 1 << 19 ) - 1,
|
|
( 1 << 20 ) - 1,
|
|
( 1 << 21 ) - 1,
|
|
( 1 << 22 ) - 1,
|
|
( 1 << 23 ) - 1,
|
|
( 1 << 24 ) - 1,
|
|
( 1 << 25 ) - 1,
|
|
( 1 << 26 ) - 1,
|
|
( 1 << 27 ) - 1,
|
|
( 1 << 28 ) - 1,
|
|
( 1 << 29 ) - 1,
|
|
( 1 << 30 ) - 1,
|
|
0x7fffffff,
|
|
0xffffffff,
|
|
};
|
|
|
|
bool CBitWrite::WriteString( const char *pStr )
|
|
{
|
|
if(pStr)
|
|
{
|
|
while( *pStr )
|
|
{
|
|
WriteChar( * ( pStr++ ) );
|
|
}
|
|
}
|
|
WriteChar( 0 );
|
|
return !IsOverflowed();
|
|
}
|
|
|
|
|
|
void CBitWrite::WriteLongLong(int64 val)
|
|
{
|
|
uint *pLongs = (uint*)&val;
|
|
|
|
// Insert the two DWORDS according to network endian
|
|
const short endianIndex = 0x0100;
|
|
byte *idx = (byte*)&endianIndex;
|
|
WriteUBitLong(pLongs[*idx++], sizeof(int32) << 3);
|
|
WriteUBitLong(pLongs[*idx], sizeof(int32) << 3);
|
|
}
|
|
|
|
bool CBitWrite::WriteBits(const void *pInData, int nBits)
|
|
{
|
|
unsigned char *pOut = (unsigned char*)pInData;
|
|
int nBitsLeft = nBits;
|
|
|
|
// Bounds checking..
|
|
if ( ( GetNumBitsWritten() + nBits) > m_nDataBits )
|
|
{
|
|
SetOverflowFlag();
|
|
CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, m_pDebugName );
|
|
return false;
|
|
}
|
|
|
|
// !! speed!! need fast paths
|
|
// write remaining bytes
|
|
while ( nBitsLeft >= 8 )
|
|
{
|
|
WriteUBitLong( *pOut, 8, false );
|
|
++pOut;
|
|
nBitsLeft -= 8;
|
|
}
|
|
|
|
// write remaining bits
|
|
if ( nBitsLeft )
|
|
{
|
|
WriteUBitLong( *pOut, nBitsLeft, false );
|
|
}
|
|
|
|
return !IsOverflowed();
|
|
}
|
|
|
|
void CBitWrite::WriteBytes( const void *pBuf, int nBytes )
|
|
{
|
|
WriteBits(pBuf, nBytes << 3);
|
|
}
|
|
|
|
void CBitWrite::WriteBitCoord (const float f)
|
|
{
|
|
int signbit = (f <= -COORD_RESOLUTION);
|
|
int intval = (int)abs(f);
|
|
int fractval = abs((int)(f*COORD_DENOMINATOR)) & (COORD_DENOMINATOR-1);
|
|
|
|
|
|
// Send the bit flags that indicate whether we have an integer part and/or a fraction part.
|
|
WriteOneBit( intval );
|
|
WriteOneBit( fractval );
|
|
|
|
if ( intval || fractval )
|
|
{
|
|
// Send the sign bit
|
|
WriteOneBit( signbit );
|
|
|
|
// Send the integer if we have one.
|
|
if ( intval )
|
|
{
|
|
// Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1]
|
|
intval--;
|
|
WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS );
|
|
}
|
|
|
|
// Send the fraction if we have one
|
|
if ( fractval )
|
|
{
|
|
WriteUBitLong( (unsigned int)fractval, COORD_FRACTIONAL_BITS );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CBitWrite::WriteBitCoordMP (const float f, EBitCoordType coordType )
|
|
{
|
|
bool bIntegral = ( coordType == kCW_Integral );
|
|
bool bLowPrecision = ( coordType == kCW_LowPrecision );
|
|
|
|
int signbit = (f <= -( bLowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION ));
|
|
int intval = (int)abs(f);
|
|
int fractval = bLowPrecision ?
|
|
( abs((int)(f*COORD_DENOMINATOR_LOWPRECISION)) & (COORD_DENOMINATOR_LOWPRECISION-1) ) :
|
|
( abs((int)(f*COORD_DENOMINATOR)) & (COORD_DENOMINATOR-1) );
|
|
|
|
bool bInBounds = intval < (1 << COORD_INTEGER_BITS_MP );
|
|
|
|
WriteOneBit( bInBounds );
|
|
|
|
if ( bIntegral )
|
|
{
|
|
// Send the sign bit
|
|
WriteOneBit( intval );
|
|
if ( intval )
|
|
{
|
|
WriteOneBit( signbit );
|
|
// Send the integer if we have one.
|
|
// Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1]
|
|
intval--;
|
|
if ( bInBounds )
|
|
{
|
|
WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS_MP );
|
|
}
|
|
else
|
|
{
|
|
WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Send the bit flags that indicate whether we have an integer part and/or a fraction part.
|
|
WriteOneBit( intval );
|
|
// Send the sign bit
|
|
WriteOneBit( signbit );
|
|
|
|
// Send the integer if we have one.
|
|
if ( intval )
|
|
{
|
|
// Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1]
|
|
intval--;
|
|
if ( bInBounds )
|
|
{
|
|
WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS_MP );
|
|
}
|
|
else
|
|
{
|
|
WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS );
|
|
}
|
|
}
|
|
WriteUBitLong( (unsigned int)fractval, bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS );
|
|
}
|
|
}
|
|
|
|
void CBitWrite::WriteBitCellCoord( const float f, int bits, EBitCoordType coordType )
|
|
{
|
|
Assert( f >= 0.0f ); // cell coords can't be negative
|
|
Assert( f < ( 1 << bits ) );
|
|
|
|
bool bIntegral = ( coordType == kCW_Integral );
|
|
bool bLowPrecision = ( coordType == kCW_LowPrecision );
|
|
|
|
int intval = (int)abs(f);
|
|
int fractval = bLowPrecision ?
|
|
( abs((int)(f*COORD_DENOMINATOR_LOWPRECISION)) & (COORD_DENOMINATOR_LOWPRECISION-1) ) :
|
|
( abs((int)(f*COORD_DENOMINATOR)) & (COORD_DENOMINATOR-1) );
|
|
|
|
if ( bIntegral )
|
|
{
|
|
WriteUBitLong( (unsigned int)intval, bits );
|
|
}
|
|
else
|
|
{
|
|
WriteUBitLong( (unsigned int)intval, bits );
|
|
WriteUBitLong( (unsigned int)fractval, bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CBitWrite::SeekToBit( int nBit )
|
|
{
|
|
TempFlush();
|
|
m_pDataOut = m_pData + ( nBit / 32 );
|
|
m_nOutBufWord = LoadLittleDWord( m_pDataOut, 0 );
|
|
m_nOutBitsAvail = 32 - ( nBit & 31 );
|
|
}
|
|
|
|
|
|
|
|
void CBitWrite::WriteBitVec3Coord( const Vector& fa )
|
|
{
|
|
int xflag, yflag, zflag;
|
|
|
|
xflag = (fa[0] >= COORD_RESOLUTION) || (fa[0] <= -COORD_RESOLUTION);
|
|
yflag = (fa[1] >= COORD_RESOLUTION) || (fa[1] <= -COORD_RESOLUTION);
|
|
zflag = (fa[2] >= COORD_RESOLUTION) || (fa[2] <= -COORD_RESOLUTION);
|
|
|
|
WriteOneBit( xflag );
|
|
WriteOneBit( yflag );
|
|
WriteOneBit( zflag );
|
|
|
|
if ( xflag )
|
|
WriteBitCoord( fa[0] );
|
|
if ( yflag )
|
|
WriteBitCoord( fa[1] );
|
|
if ( zflag )
|
|
WriteBitCoord( fa[2] );
|
|
}
|
|
|
|
void CBitWrite::WriteBitNormal( float f )
|
|
{
|
|
int signbit = (f <= -NORMAL_RESOLUTION);
|
|
|
|
// NOTE: Since +/-1 are valid values for a normal, I'm going to encode that as all ones
|
|
unsigned int fractval = abs( (int)(f*NORMAL_DENOMINATOR) );
|
|
|
|
// clamp..
|
|
if (fractval > NORMAL_DENOMINATOR)
|
|
fractval = NORMAL_DENOMINATOR;
|
|
|
|
// Send the sign bit
|
|
WriteOneBit( signbit );
|
|
|
|
// Send the fractional component
|
|
WriteUBitLong( fractval, NORMAL_FRACTIONAL_BITS );
|
|
}
|
|
|
|
void CBitWrite::WriteBitVec3Normal( const Vector& fa )
|
|
{
|
|
int xflag, yflag;
|
|
|
|
xflag = (fa[0] >= NORMAL_RESOLUTION) || (fa[0] <= -NORMAL_RESOLUTION);
|
|
yflag = (fa[1] >= NORMAL_RESOLUTION) || (fa[1] <= -NORMAL_RESOLUTION);
|
|
|
|
WriteOneBit( xflag );
|
|
WriteOneBit( yflag );
|
|
|
|
if ( xflag )
|
|
WriteBitNormal( fa[0] );
|
|
if ( yflag )
|
|
WriteBitNormal( fa[1] );
|
|
|
|
// Write z sign bit
|
|
int signbit = (fa[2] <= -NORMAL_RESOLUTION);
|
|
WriteOneBit( signbit );
|
|
}
|
|
|
|
void CBitWrite::WriteBitAngle( float fAngle, int numbits )
|
|
{
|
|
|
|
unsigned int shift = GetBitForBitnum(numbits);
|
|
unsigned int mask = shift - 1;
|
|
|
|
int d = (int)( (fAngle / 360.0) * shift );
|
|
d &= mask;
|
|
|
|
WriteUBitLong((unsigned int)d, numbits);
|
|
}
|
|
|
|
bool CBitWrite::WriteBitsFromBuffer( bf_read *pIn, int nBits )
|
|
{
|
|
// This could be optimized a little by
|
|
while ( nBits > 32 )
|
|
{
|
|
WriteUBitLong( pIn->ReadUBitLong( 32 ), 32 );
|
|
nBits -= 32;
|
|
}
|
|
|
|
WriteUBitLong( pIn->ReadUBitLong( nBits ), nBits );
|
|
return !IsOverflowed() && !pIn->IsOverflowed();
|
|
}
|
|
|
|
void CBitWrite::WriteBitAngles( const QAngle& fa )
|
|
{
|
|
// FIXME:
|
|
Vector tmp( fa.x, fa.y, fa.z );
|
|
WriteBitVec3Coord( tmp );
|
|
}
|
|
|
|
bool CBitRead::Seek( int nPosition )
|
|
{
|
|
bool bSucc = true;
|
|
if ( nPosition < 0 || nPosition > m_nDataBits)
|
|
{
|
|
SetOverflowFlag();
|
|
bSucc = false;
|
|
nPosition = m_nDataBits;
|
|
}
|
|
int nHead = m_nDataBytes & 3; // non-multiple-of-4 bytes at head of buffer. We put the "round off"
|
|
// at the head to make reading and detecting the end efficient.
|
|
|
|
int nByteOfs = nPosition / 8;
|
|
if ( ( m_nDataBytes < 4 ) || ( nHead && ( nByteOfs < nHead ) ) )
|
|
{
|
|
// partial first dword
|
|
uint8 const *pPartial = ( uint8 const *) m_pData;
|
|
if ( m_pData )
|
|
{
|
|
m_nInBufWord = *( pPartial++ );
|
|
if ( nHead > 1 )
|
|
m_nInBufWord |= ( *pPartial++ ) << 8;
|
|
if ( nHead > 2 )
|
|
m_nInBufWord |= ( *pPartial++ ) << 16;
|
|
}
|
|
m_pDataIn = ( uint32 const * ) pPartial;
|
|
m_nInBufWord >>= ( nPosition & 31 );
|
|
m_nBitsAvail = ( nHead << 3 ) - ( nPosition & 31 );
|
|
}
|
|
else
|
|
{
|
|
int nAdjPosition = nPosition - ( nHead << 3 );
|
|
m_pDataIn = reinterpret_cast<uint32 const *> (
|
|
reinterpret_cast<uint8 const *>( m_pData ) + ( ( nAdjPosition / 32 ) << 2 ) + nHead );
|
|
if ( m_pData )
|
|
{
|
|
m_nBitsAvail = 32;
|
|
GrabNextDWord();
|
|
}
|
|
else
|
|
{
|
|
m_nInBufWord = 0;
|
|
m_nBitsAvail = 1;
|
|
}
|
|
m_nInBufWord >>= ( nAdjPosition & 31 );
|
|
m_nBitsAvail = MIN( m_nBitsAvail, 32 - ( nAdjPosition & 31 ) ); // in case grabnextdword overflowed
|
|
}
|
|
return bSucc;
|
|
}
|
|
|
|
|
|
void CBitRead::StartReading( const void *pData, int nBytes, int iStartBit, int nBits )
|
|
{
|
|
DEBUG_LINK_CHECK;
|
|
// Make sure it's dword aligned and padded.
|
|
Assert(((uintp)pData & 3) == 0);
|
|
m_pData = (uint32 *) pData;
|
|
m_pDataIn = m_pData;
|
|
m_nDataBytes = nBytes;
|
|
|
|
if ( nBits == -1 )
|
|
{
|
|
m_nDataBits = nBytes << 3;
|
|
}
|
|
else
|
|
{
|
|
Assert( nBits <= nBytes*8 );
|
|
m_nDataBits = nBits;
|
|
}
|
|
m_bOverflow = false;
|
|
m_pBufferEnd = reinterpret_cast<uint32 const *> ( reinterpret_cast< uint8 const *> (m_pData) + nBytes );
|
|
if ( m_pData )
|
|
Seek( iStartBit );
|
|
|
|
}
|
|
|
|
bool CBitRead::ReadString( char *pStr, int maxLen, bool bLine, int *pOutNumChars )
|
|
{
|
|
Assert( maxLen != 0 );
|
|
|
|
bool bTooSmall = false;
|
|
int iChar = 0;
|
|
while(1)
|
|
{
|
|
char val = ReadChar();
|
|
if ( val == 0 )
|
|
break;
|
|
else if ( bLine && val == '\n' )
|
|
break;
|
|
|
|
if ( iChar < (maxLen-1) )
|
|
{
|
|
pStr[iChar] = val;
|
|
++iChar;
|
|
}
|
|
else
|
|
{
|
|
bTooSmall = true;
|
|
}
|
|
}
|
|
|
|
// Make sure it's null-terminated.
|
|
Assert( iChar < maxLen );
|
|
pStr[iChar] = 0;
|
|
|
|
if ( pOutNumChars )
|
|
*pOutNumChars = iChar;
|
|
|
|
return !IsOverflowed() && !bTooSmall;
|
|
}
|
|
|
|
bool CBitRead::ReadWString( OUT_Z_CAP(maxLenInChars) wchar_t *pStr, int maxLenInChars, bool bLine, int *pOutNumChars )
|
|
{
|
|
Assert( maxLenInChars != 0 );
|
|
|
|
bool bTooSmall = false;
|
|
int iChar = 0;
|
|
while(1)
|
|
{
|
|
wchar val = ReadShort();
|
|
if ( val == 0 )
|
|
break;
|
|
else if ( bLine && val == L'\n' )
|
|
break;
|
|
|
|
if ( iChar < (maxLenInChars-1) )
|
|
{
|
|
pStr[iChar] = val;
|
|
++iChar;
|
|
}
|
|
else
|
|
{
|
|
bTooSmall = true;
|
|
}
|
|
}
|
|
|
|
// Make sure it's null-terminated.
|
|
Assert( iChar < maxLenInChars );
|
|
pStr[iChar] = 0;
|
|
|
|
if ( pOutNumChars )
|
|
*pOutNumChars = iChar;
|
|
|
|
return !IsOverflowed() && !bTooSmall;
|
|
}
|
|
|
|
char* CBitRead::ReadAndAllocateString( bool *pOverflow )
|
|
{
|
|
char str[2048];
|
|
|
|
int nChars;
|
|
bool bOverflow = !ReadString( str, sizeof( str ), false, &nChars );
|
|
if ( pOverflow )
|
|
*pOverflow = bOverflow;
|
|
|
|
// Now copy into the output and return it;
|
|
char *pRet = new char[ nChars + 1 ];
|
|
for ( int i=0; i <= nChars; i++ )
|
|
pRet[i] = str[i];
|
|
|
|
return pRet;
|
|
}
|
|
|
|
int64 CBitRead::ReadLongLong( void )
|
|
{
|
|
int64 retval;
|
|
uint *pLongs = (uint*)&retval;
|
|
|
|
// Read the two DWORDs according to network endian
|
|
const short endianIndex = 0x0100;
|
|
byte *idx = (byte*)&endianIndex;
|
|
pLongs[*idx++] = ReadUBitLong(sizeof(int32) << 3);
|
|
pLongs[*idx] = ReadUBitLong(sizeof(int32) << 3);
|
|
return retval;
|
|
}
|
|
|
|
// Read 1-5 bytes in order to extract a 32-bit unsigned value from the
|
|
// stream. 7 data bits are extracted from each byte with the 8th bit used
|
|
// to indicate whether the loop should continue.
|
|
// This allows variable size numbers to be stored with tolerable
|
|
// efficiency. Numbers sizes that can be stored for various numbers of
|
|
// encoded bits are:
|
|
// 8-bits: 0-127
|
|
// 16-bits: 128-16383
|
|
// 24-bits: 16384-2097151
|
|
// 32-bits: 2097152-268435455
|
|
// 40-bits: 268435456-0xFFFFFFFF
|
|
uint32 CBitRead::ReadVarInt32()
|
|
{
|
|
uint32 result = 0;
|
|
int count = 0;
|
|
uint32 b;
|
|
|
|
do
|
|
{
|
|
if ( count == bitbuf::kMaxVarint32Bytes )
|
|
{
|
|
return result;
|
|
}
|
|
b = ReadUBitLong( 8 );
|
|
result |= (b & 0x7F) << (7 * count);
|
|
++count;
|
|
} while (b & 0x80);
|
|
|
|
return result;
|
|
}
|
|
|
|
uint64 CBitRead::ReadVarInt64()
|
|
{
|
|
uint64 result = 0;
|
|
int count = 0;
|
|
uint64 b;
|
|
|
|
do
|
|
{
|
|
if ( count == bitbuf::kMaxVarintBytes )
|
|
{
|
|
return result;
|
|
}
|
|
b = ReadUBitLong( 8 );
|
|
result |= static_cast<uint64>(b & 0x7F) << (7 * count);
|
|
++count;
|
|
} while (b & 0x80);
|
|
|
|
return result;
|
|
}
|
|
|
|
void CBitRead::ReadBits(void *pOutData, int nBits)
|
|
{
|
|
unsigned char *pOut = (unsigned char*)pOutData;
|
|
int nBitsLeft = nBits;
|
|
|
|
|
|
// align output to dword boundary
|
|
while( ((uintp)pOut & 3) != 0 && nBitsLeft >= 8 )
|
|
{
|
|
*pOut = (unsigned char)ReadUBitLong(8);
|
|
++pOut;
|
|
nBitsLeft -= 8;
|
|
}
|
|
|
|
// X360TBD: Can't read dwords in ReadBits because they'll get swapped
|
|
if ( IsPC() )
|
|
{
|
|
// read dwords
|
|
while ( nBitsLeft >= 32 )
|
|
{
|
|
*((uint32*)pOut) = ReadUBitLong(32);
|
|
pOut += sizeof(uint32);
|
|
nBitsLeft -= 32;
|
|
}
|
|
}
|
|
|
|
// read remaining bytes
|
|
while ( nBitsLeft >= 8 )
|
|
{
|
|
*pOut = ReadUBitLong(8);
|
|
++pOut;
|
|
nBitsLeft -= 8;
|
|
}
|
|
|
|
// read remaining bits
|
|
if ( nBitsLeft )
|
|
{
|
|
*pOut = ReadUBitLong(nBitsLeft);
|
|
}
|
|
|
|
}
|
|
|
|
bool CBitRead::ReadBytes(void *pOut, int nBytes)
|
|
{
|
|
ReadBits(pOut, nBytes << 3);
|
|
return !IsOverflowed();
|
|
}
|
|
|
|
float CBitRead::ReadBitAngle( int numbits )
|
|
{
|
|
float shift = (float)( GetBitForBitnum(numbits) );
|
|
|
|
int i = ReadUBitLong( numbits );
|
|
float fReturn = (float)i * (360.0 / shift);
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
// Basic Coordinate Routines (these contain bit-field size AND fixed point scaling constants)
|
|
float CBitRead::ReadBitCoord (void)
|
|
{
|
|
int intval=0,fractval=0,signbit=0;
|
|
float value = 0.0;
|
|
|
|
|
|
// Read the required integer and fraction flags
|
|
intval = ReadOneBit();
|
|
fractval = ReadOneBit();
|
|
|
|
// If we got either parse them, otherwise it's a zero.
|
|
if ( intval || fractval )
|
|
{
|
|
// Read the sign bit
|
|
signbit = ReadOneBit();
|
|
|
|
// If there's an integer, read it in
|
|
if ( intval )
|
|
{
|
|
// Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE]
|
|
intval = ReadUBitLong( COORD_INTEGER_BITS ) + 1;
|
|
}
|
|
|
|
// If there's a fraction, read it in
|
|
if ( fractval )
|
|
{
|
|
fractval = ReadUBitLong( COORD_FRACTIONAL_BITS );
|
|
}
|
|
|
|
// Calculate the correct floating point value
|
|
value = intval + ((float)fractval * COORD_RESOLUTION);
|
|
|
|
// Fixup the sign if negative.
|
|
if ( signbit )
|
|
value = -value;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
float CBitRead::ReadBitCoordMP( EBitCoordType coordType )
|
|
{
|
|
bool bIntegral = ( coordType == kCW_Integral );
|
|
bool bLowPrecision = ( coordType == kCW_LowPrecision );
|
|
|
|
int intval=0,fractval=0,signbit=0;
|
|
float value = 0.0;
|
|
|
|
bool bInBounds = ReadOneBit() ? true : false;
|
|
|
|
if ( bIntegral )
|
|
{
|
|
// Read the required integer and fraction flags
|
|
intval = ReadOneBit();
|
|
// If we got either parse them, otherwise it's a zero.
|
|
if ( intval )
|
|
{
|
|
// Read the sign bit
|
|
signbit = ReadOneBit();
|
|
|
|
// If there's an integer, read it in
|
|
// Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE]
|
|
if ( bInBounds )
|
|
{
|
|
value = ReadUBitLong( COORD_INTEGER_BITS_MP ) + 1;
|
|
}
|
|
else
|
|
{
|
|
value = ReadUBitLong( COORD_INTEGER_BITS ) + 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Read the required integer and fraction flags
|
|
intval = ReadOneBit();
|
|
|
|
// Read the sign bit
|
|
signbit = ReadOneBit();
|
|
|
|
// If we got either parse them, otherwise it's a zero.
|
|
if ( intval )
|
|
{
|
|
if ( bInBounds )
|
|
{
|
|
intval = ReadUBitLong( COORD_INTEGER_BITS_MP ) + 1;
|
|
}
|
|
else
|
|
{
|
|
intval = ReadUBitLong( COORD_INTEGER_BITS ) + 1;
|
|
}
|
|
}
|
|
|
|
// If there's a fraction, read it in
|
|
fractval = ReadUBitLong( bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS );
|
|
|
|
// Calculate the correct floating point value
|
|
value = intval + ((float)fractval * ( bLowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION ) );
|
|
}
|
|
|
|
// Fixup the sign if negative.
|
|
if ( signbit )
|
|
value = -value;
|
|
|
|
return value;
|
|
}
|
|
|
|
float CBitRead::ReadBitCellCoord( int bits, EBitCoordType coordType )
|
|
{
|
|
#if defined( BB_PROFILING )
|
|
VPROF( "bf_write::ReadBitCoordMP" );
|
|
#endif
|
|
bool bIntegral = ( coordType == kCW_Integral );
|
|
bool bLowPrecision = ( coordType == kCW_LowPrecision );
|
|
|
|
int intval=0,fractval=0;
|
|
float value = 0.0;
|
|
|
|
if ( bIntegral )
|
|
{
|
|
value = ReadUBitLong( bits );
|
|
}
|
|
else
|
|
{
|
|
intval = ReadUBitLong( bits );
|
|
|
|
// If there's a fraction, read it in
|
|
fractval = ReadUBitLong( bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS );
|
|
|
|
// Calculate the correct floating point value
|
|
value = intval + ((float)fractval * ( bLowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION ) );
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
void CBitRead::ReadBitVec3Coord( Vector& fa )
|
|
{
|
|
int xflag, yflag, zflag;
|
|
|
|
// This vector must be initialized! Otherwise, If any of the flags aren't set,
|
|
// the corresponding component will not be read and will be stack garbage.
|
|
fa.Init( 0, 0, 0 );
|
|
|
|
xflag = ReadOneBit();
|
|
yflag = ReadOneBit();
|
|
zflag = ReadOneBit();
|
|
|
|
if ( xflag )
|
|
fa[0] = ReadBitCoord();
|
|
if ( yflag )
|
|
fa[1] = ReadBitCoord();
|
|
if ( zflag )
|
|
fa[2] = ReadBitCoord();
|
|
}
|
|
|
|
float CBitRead::ReadBitNormal (void)
|
|
{
|
|
// Read the sign bit
|
|
int signbit = ReadOneBit();
|
|
|
|
// Read the fractional part
|
|
unsigned int fractval = ReadUBitLong( NORMAL_FRACTIONAL_BITS );
|
|
|
|
// Calculate the correct floating point value
|
|
float value = (float)fractval * NORMAL_RESOLUTION;
|
|
|
|
// Fixup the sign if negative.
|
|
if ( signbit )
|
|
value = -value;
|
|
|
|
return value;
|
|
}
|
|
|
|
void CBitRead::ReadBitVec3Normal( Vector& fa )
|
|
{
|
|
int xflag = ReadOneBit();
|
|
int yflag = ReadOneBit();
|
|
|
|
if (xflag)
|
|
fa[0] = ReadBitNormal();
|
|
else
|
|
fa[0] = 0.0f;
|
|
|
|
if (yflag)
|
|
fa[1] = ReadBitNormal();
|
|
else
|
|
fa[1] = 0.0f;
|
|
|
|
// The first two imply the third (but not its sign)
|
|
int znegative = ReadOneBit();
|
|
|
|
float fafafbfb = fa[0] * fa[0] + fa[1] * fa[1];
|
|
if (fafafbfb < 1.0f)
|
|
fa[2] = sqrt( 1.0f - fafafbfb );
|
|
else
|
|
fa[2] = 0.0f;
|
|
|
|
if (znegative)
|
|
fa[2] = -fa[2];
|
|
}
|
|
|
|
void CBitRead::ReadBitAngles( QAngle& fa )
|
|
{
|
|
Vector tmp;
|
|
ReadBitVec3Coord( tmp );
|
|
fa.Init( tmp.x, tmp.y, tmp.z );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
/*
|
|
|
|
// Tests
|
|
|
|
#define TEST_BITBUF
|
|
|
|
#ifndef TEST_BITBUF
|
|
|
|
void TestBitBufs()
|
|
{
|
|
}
|
|
|
|
#else //TEST_BITBUF
|
|
|
|
enum EBitBufTestFields
|
|
{
|
|
kBBTF_ULONG,
|
|
kBBTF_BYTES,
|
|
#if 0
|
|
// kBBTF_UBVAR,
|
|
kBBTF_FLOAT,
|
|
kBBTF_CHAR,
|
|
kBBTF_BYTE,
|
|
kBBTF_SHORT,
|
|
kBBTF_STRING,
|
|
kBBTF_LONGLONG,
|
|
#endif
|
|
|
|
kBBTF_Count
|
|
};
|
|
|
|
static char bitbuf_string[] = "Life's but a walking shadow, a poor player.";
|
|
static char bitsbuf[ sizeof( bitbuf_string ) ];
|
|
|
|
template < class TWriter >
|
|
float TestBitBufferWriter( void *buffer, int bufsize, int seed, int numtests, bool log = false )
|
|
{
|
|
CFastTimer timer;
|
|
|
|
timer.Start();
|
|
|
|
TWriter writer( "TestBufferWriter", buffer, bufsize );
|
|
|
|
// Fill it up with the writer
|
|
RandomSeed( seed );
|
|
|
|
int bitTotal = 0;
|
|
|
|
for ( int i = 0; i < numtests; ++i )
|
|
{
|
|
if ( writer.IsOverflowed() )
|
|
{
|
|
printf("writer: OVERFLOW!" );
|
|
}
|
|
|
|
if ( writer.GetNumBitsWritten() != bitTotal )
|
|
{
|
|
printf("writer: bitTotal MISMATCH!\n");
|
|
}
|
|
|
|
int testtype = RandomInt( 0, kBBTF_Count - 1 );
|
|
switch ( testtype )
|
|
{
|
|
case kBBTF_ULONG:
|
|
{
|
|
int bits = RandomInt( 0, ( sizeof( uint32 ) << 3 ) - 1 );
|
|
uint32 n = (uint32)RandomInt( 0, 0x7fff );
|
|
if ( bits )
|
|
{
|
|
n &= (1 << bits) - 1;
|
|
}
|
|
else
|
|
{
|
|
bits = 32;
|
|
}
|
|
if (log) printf("\t%3d: write ULONG: %u, %d bits\n", i, n, bits );
|
|
|
|
writer.WriteUBitLong( n, bits );
|
|
|
|
bitTotal += bits;
|
|
}
|
|
break;
|
|
|
|
case kBBTF_BYTES:
|
|
{
|
|
int bytes = RandomInt( 1, sizeof( bitsbuf ) );
|
|
if (log) printf("\t%3d: write BYTES: %d bytes\n", i, bytes );
|
|
writer.WriteBytes( bitsbuf, bytes );
|
|
|
|
bitTotal += bytes << 3;
|
|
}
|
|
break;
|
|
#if 0
|
|
|
|
case kBBTF_SLONG:
|
|
{
|
|
int bits = RandomInt( 2, sizeof( int32 ) << 3 );
|
|
int32 n = (int32)RandomInt( 0, 0x7fff ) & ( ( 1 << ( bits - 1 ) ) - 1 );
|
|
if ( RandomInt( 0, 1 ) < 1 )
|
|
{
|
|
n = -n;
|
|
}
|
|
if (log) printf("\t%3d: write SLONG: %d, %d bits\n", i, n, bits );
|
|
|
|
writer.WriteSBitLong( n, bits );
|
|
|
|
bitTotal += bits;
|
|
}
|
|
break;
|
|
|
|
|
|
case kBBTF_UBVAR:
|
|
{
|
|
unsigned int n = (unsigned int)RandomInt( 0, 0x7fff );
|
|
if (log) printf("\t%3d: write UBVAR: %u\n", i, n );
|
|
writer.WriteUBitVar( n );
|
|
}
|
|
break;
|
|
|
|
case kBBTF_FLOAT:
|
|
{
|
|
float n = RandomFloat( 0.0f, FLT_MAX );
|
|
if (log) printf("\t%3d: write FLOAT: %f\n", i, n );
|
|
writer.WriteFloat( n );
|
|
|
|
bitTotal += sizeof(float)<<3;;
|
|
}
|
|
break;
|
|
|
|
case kBBTF_CHAR:
|
|
{
|
|
char n = (char)RandomInt( 0, 0x7f );
|
|
if (log) printf("\t%3d: write CHAR: %d\n", i, n );
|
|
writer.WriteChar( n );
|
|
bitTotal += sizeof(char)<<3;;
|
|
}
|
|
break;
|
|
|
|
case kBBTF_BYTE:
|
|
{
|
|
unsigned char n = (unsigned char)RandomInt( 0, 0xff );
|
|
if (log) printf("\t%3d: write BYTE: %d\n", i, n );
|
|
writer.WriteByte( n );
|
|
bitTotal += sizeof(unsigned char)<<3;;
|
|
}
|
|
break;
|
|
|
|
case kBBTF_SHORT:
|
|
{
|
|
short n = (short)RandomInt( 0, 0x7fff );
|
|
if (log) printf("\t%3d: write SHORT: %d\n", i, n );
|
|
writer.WriteShort( n );
|
|
bitTotal += sizeof(short)<<3;;
|
|
}
|
|
break;
|
|
|
|
|
|
case kBBTF_STRING:
|
|
{
|
|
writer.WriteString( bitbuf_string );
|
|
if (log) printf("\t%3d: write STRING\n", i );
|
|
bitTotal += (sizeof(char)<<3) * ( strlen( bitbuf_string ) + 1 );
|
|
}
|
|
break;
|
|
|
|
case kBBTF_LONGLONG:
|
|
{
|
|
int64 low = RandomInt( 0, 0x7fff );
|
|
int64 high = RandomInt( 0, 0x7fff );
|
|
int64 n = (high << 32) | low;
|
|
if (log) printf("\t%3d: write LONGLONG: %lld\n", i, n );
|
|
writer.WriteLongLong( n );
|
|
bitTotal += sizeof(int64)<<3;
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
writer.GetData(); // insure a flush
|
|
|
|
timer.End();
|
|
|
|
return timer.GetDuration().GetMicrosecondsF();
|
|
}
|
|
|
|
template < class TReader >
|
|
float TestBitBufferReader( void *buffer, int bufsize, int seed, int numtests, bool log = false )
|
|
{
|
|
CFastTimer timer;
|
|
|
|
timer.Start();
|
|
|
|
TReader reader( "TestBufferReader", buffer, bufsize );
|
|
|
|
// And let's read it back to ensure it all got written correctly
|
|
// Fill it up with the writer
|
|
RandomSeed( seed );
|
|
|
|
for ( int i = 0; i < numtests; ++i )
|
|
{
|
|
int testtype = RandomInt( 0, kBBTF_Count - 1 );
|
|
switch ( testtype )
|
|
{
|
|
case kBBTF_ULONG:
|
|
{
|
|
int bits = RandomInt( 0, ( sizeof( uint32 ) << 3 ) - 1 );
|
|
uint32 n = (uint32)RandomInt( 0, 0x7fff );
|
|
if ( bits )
|
|
{
|
|
n &= (1 << bits) - 1;
|
|
}
|
|
else
|
|
{
|
|
bits = 32;
|
|
}
|
|
|
|
uint32 v = reader.ReadUBitLong( bits );
|
|
|
|
if (log) printf("\t%3d: read ULONG: %u, %d bits, GOT: %u\n", i, n, bits, v );
|
|
if ( v != n )
|
|
{
|
|
printf("\t%3d: Mismatched ULONG: read %u instead of %u\n", i, v, n );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kBBTF_BYTES:
|
|
{
|
|
int bytes = RandomInt( 1, sizeof( bitsbuf ) );
|
|
|
|
char readbuf[ sizeof( bitsbuf ) ];
|
|
reader.ReadBytes( readbuf, bytes );
|
|
|
|
if (log) printf("\t%3d: read BYTES: %d bytes\n", i, bytes );
|
|
if ( Q_memcmp( bitsbuf, readbuf, bytes ) )
|
|
{
|
|
printf("\t%3d: Mismatched BYTES\n", i);
|
|
}
|
|
}
|
|
break;
|
|
#if 0
|
|
|
|
case kBBTF_BYTE:
|
|
{
|
|
unsigned char n = (unsigned char)RandomInt( 0, 0xff );
|
|
|
|
unsigned char v = reader.ReadByte();
|
|
if (log) printf("\t%3d: read BYTE: %d, GOT: %d\n", i, n, v );
|
|
if ( v != n )
|
|
{
|
|
printf("\t%3d: Mismatched BYTE: read %d instead of %d\n", i, v, n );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kBBTF_SLONG:
|
|
{
|
|
int bits = RandomInt( 2, sizeof( int32 ) << 3 );
|
|
int32 n = (int32)RandomInt( 0, 0x7fff ) & ( ( 1 << ( bits - 1 ) ) - 1 );
|
|
if ( RandomInt( 0, 1 ) < 1 )
|
|
{
|
|
n = -n;
|
|
}
|
|
|
|
int32 v = reader.ReadSBitLong( bits );
|
|
if (log) printf("\t%3d: read SLONG: %d, %d bits, GOT: %d\n", i, n, bits, v );
|
|
if ( v != n )
|
|
{
|
|
printf("\t%3d: Mismatched SLONG: read %d instead of %d\n", i, v, n );
|
|
}
|
|
}
|
|
break;
|
|
case kBBTF_UBVAR:
|
|
{
|
|
unsigned int n = (unsigned int)RandomInt( 0, 0x7fff );
|
|
|
|
unsigned int v = reader.ReadUBitVar();
|
|
if (log) printf("\t%3d: read UBVAR: %u, GOT: %u\n", i, n, v );
|
|
if ( v != n )
|
|
{
|
|
printf("\t%3d: Mismatched UBVAR: read %u instead of %u\n", i, v, n );
|
|
}
|
|
}
|
|
break;
|
|
case kBBTF_FLOAT:
|
|
{
|
|
float n = RandomFloat( 0.0f, FLT_MAX );
|
|
|
|
float v = reader.ReadFloat();
|
|
if (log) printf("\t%3d: read FLOAT: %f, GOT: %f\n", i, n, v );
|
|
// if ( v != n )
|
|
// {
|
|
// printf("\t%3d: Mismatched FLOAT: read %f instead of %f (d=%f)\n", i, v, n, v - n );
|
|
// }
|
|
}
|
|
break;
|
|
|
|
case kBBTF_CHAR:
|
|
{
|
|
char n = (char)RandomInt( 0, 0x7f );
|
|
|
|
char v = reader.ReadChar();
|
|
if (log) printf("\t%3d: read CHAR: %d, GOT: %d\n", i, n, v );
|
|
if ( v != n )
|
|
{
|
|
printf("\t%3d: Mismatched CHAR: read %d instead of %d\n", i, v, n );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kBBTF_SHORT:
|
|
{
|
|
short n = (short)RandomInt( 0, 0x7fff );
|
|
|
|
short v = reader.ReadShort();
|
|
if (log) printf("\t%3d: read SHORT: %d, GOT: %d\n", i, n, v );
|
|
if ( v != n )
|
|
{
|
|
printf("\t%3d: Mismatched SHORT: read %d instead of %d\n", i, v, n );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kBBTF_STRING:
|
|
{
|
|
char readbuf[ sizeof( bitbuf_string ) ];
|
|
reader.ReadString( readbuf, sizeof( readbuf ) );
|
|
if (log) printf("\t%3d: read STRING\n", i );
|
|
if ( Q_strcmp( bitbuf_string, readbuf ) )
|
|
{
|
|
printf("\t%3d: Mismatched STRING\n", i);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kBBTF_LONGLONG:
|
|
{
|
|
int64 low = RandomInt( 0, 0x7fff );
|
|
int64 high = RandomInt( 0, 0x7fff );
|
|
int64 n = (high << 32) | low;
|
|
|
|
int64 v = reader.ReadLongLong();
|
|
if (log) printf("\t%3d: read LONGLONG: %lld, GOT: %lld\n", i, n, v );
|
|
if ( v != n )
|
|
{
|
|
printf("\t%3d: Mismatched LONGLONG: read %lld instead of %lld\n", i, v, n );
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
timer.End();
|
|
|
|
return timer.GetDuration().GetMicrosecondsF();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TestBitBufs()
|
|
{
|
|
const int repeatCount = 1024;
|
|
const int testItemCount = 1024;
|
|
const bool debugWriteReads = false;
|
|
|
|
size_t bufsize = 1024*1024;
|
|
unsigned char *buffer = (unsigned char *)malloc( bufsize );
|
|
|
|
{
|
|
bf_write bitsbuf_writer( bitsbuf, sizeof(bitsbuf) );
|
|
bitsbuf_writer.WriteBytes( bitbuf_string, sizeof(bitsbuf) );
|
|
}
|
|
|
|
{
|
|
CFastTimer timer;
|
|
printf("TestBuffer< bf_write, bf_read >: START\n" );
|
|
timer.Start();
|
|
float avgTotalWrite = 0.0f;
|
|
float avgTotalRead = 0.0f;
|
|
int seed = 1;
|
|
for ( int count = 0; count < repeatCount; ++count, ++seed )
|
|
{
|
|
V_memset( buffer, 0, bufsize );
|
|
|
|
avgTotalWrite += TestBitBufferWriter< bf_write >( buffer, bufsize, seed, testItemCount, debugWriteReads );
|
|
avgTotalRead += TestBitBufferReader< bf_read >( buffer, bufsize, seed, testItemCount, debugWriteReads );
|
|
}
|
|
timer.End();
|
|
printf("TestBuffer< bf_write, bf_read >: END: %d times, total %4.4fus, average write %4.4f, average read %4.4f\n",
|
|
repeatCount, timer.GetDuration().GetMicrosecondsF(), avgTotalWrite / repeatCount, avgTotalRead / repeatCount );
|
|
}
|
|
if ( 1 )
|
|
{
|
|
CFastTimer timer;
|
|
printf("TestBuffer< CBitWrite, bf_read >: START\n" );
|
|
timer.Start();
|
|
float avgTotalWrite = 0.0f;
|
|
float avgTotalRead = 0.0f;
|
|
int seed = 1;
|
|
for ( int count = 0; count < repeatCount; ++count, ++seed )
|
|
{
|
|
V_memset( buffer, 0, bufsize );
|
|
|
|
avgTotalWrite += TestBitBufferWriter< CBitWrite >( buffer, bufsize, seed, testItemCount, debugWriteReads );
|
|
avgTotalRead += TestBitBufferReader< bf_read >( buffer, bufsize, seed, testItemCount, debugWriteReads );
|
|
}
|
|
timer.End();
|
|
printf("TestBuffer< CBitWrite, bf_read >: END: %d times, total %4.4fus, average write %4.4f, average read %4.4f\n",
|
|
repeatCount, timer.GetDuration().GetMicrosecondsF(), avgTotalWrite / repeatCount, avgTotalRead / repeatCount );
|
|
}
|
|
|
|
free( buffer );
|
|
}
|
|
|
|
#endif //TEST_BITBUF
|
|
*/
|
|
|