source-engine/tier1/bitbuf.cpp

1491 lines
32 KiB
C++
Raw Permalink Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright 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"
// 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
#if _WIN32
#define FAST_BIT_SCAN 1
#if _X360
#define CountLeadingZeros(x) _CountLeadingZeros(x)
inline unsigned int CountTrailingZeros( unsigned int elem )
{
// this implements CountTrailingZeros() / BitScanForward()
unsigned int mask = elem-1;
unsigned int comp = ~elem;
elem = mask & comp;
return (32 - _CountLeadingZeros(elem));
}
#else
#include <intrin.h>
#pragma intrinsic(_BitScanReverse)
#pragma intrinsic(_BitScanForward)
inline unsigned int CountLeadingZeros(unsigned int x)
{
2022-02-23 11:50:30 +00:00
uint32 firstBit;
2022-07-27 09:58:56 +00:00
if ( _BitScanReverse((unsigned long*)&firstBit,x) )
2020-04-22 16:56:21 +00:00
return 31 - firstBit;
return 32;
}
inline unsigned int CountTrailingZeros(unsigned int elem)
{
2022-02-23 11:50:30 +00:00
uint32 out;
2022-07-27 09:58:56 +00:00
if ( _BitScanForward((unsigned long*)&out, elem) )
2020-04-22 16:56:21 +00:00
return out;
return 32;
}
#endif
#else
#define FAST_BIT_SCAN 0
#endif
static BitBufErrorHandler g_BitBufErrorHandler = 0;
2020-11-18 20:03:50 +00:00
inline unsigned int BitForBitnum(int bitnum)
2020-04-22 16:56:21 +00:00
{
return GetBitForBitnum(bitnum);
}
void InternalBitBufErrorHandler( BitBufErrorType errorType, const char *pDebugName )
{
if ( g_BitBufErrorHandler )
g_BitBufErrorHandler( errorType, pDebugName );
}
void SetBitBufErrorHandler( BitBufErrorHandler fn )
{
g_BitBufErrorHandler = fn;
}
// #define BB_PROFILING
2022-02-23 11:50:30 +00:00
uint32 g_LittleBits[32];
2020-04-22 16:56:21 +00:00
// Precalculated bit masks for WriteUBitLong. Using these tables instead of
// doing the calculations gives a 33% speedup in WriteUBitLong.
2022-02-23 11:50:30 +00:00
uint32 g_BitWriteMasks[32][33];
2020-04-22 16:56:21 +00:00
// (1 << i) - 1
2022-02-23 11:50:30 +00:00
uint32 g_ExtraMasks[33];
2020-04-22 16:56:21 +00:00
class CBitWriteMasksInit
{
public:
CBitWriteMasksInit()
{
for( unsigned int startbit=0; startbit < 32; startbit++ )
{
for( unsigned int nBitsLeft=0; nBitsLeft < 33; nBitsLeft++ )
{
unsigned int endbit = startbit + nBitsLeft;
g_BitWriteMasks[startbit][nBitsLeft] = BitForBitnum(startbit) - 1;
if(endbit < 32)
g_BitWriteMasks[startbit][nBitsLeft] |= ~(BitForBitnum(endbit) - 1);
}
}
for ( unsigned int maskBit=0; maskBit < 32; maskBit++ )
g_ExtraMasks[maskBit] = BitForBitnum(maskBit) - 1;
2022-02-23 11:50:30 +00:00
g_ExtraMasks[32] = ~0u;
2020-04-22 16:56:21 +00:00
for ( unsigned int littleBit=0; littleBit < 32; littleBit++ )
StoreLittleDWord( &g_LittleBits[littleBit], 0, 1u<<littleBit );
2020-11-18 14:30:58 +00:00
}
2020-04-22 16:56:21 +00:00
};
static CBitWriteMasksInit g_BitWriteMasksInit;
// ---------------------------------------------------------------------------------------- //
// bf_write
// ---------------------------------------------------------------------------------------- //
bf_write::bf_write()
{
m_pData = NULL;
m_nDataBytes = 0;
m_nDataBits = -1; // set to -1 so we generate overflow on any operation
m_iCurBit = 0;
m_bOverflow = false;
m_bAssertOnOverflow = true;
m_pDebugName = NULL;
}
bf_write::bf_write( const char *pDebugName, void *pData, int nBytes, int nBits )
{
m_bAssertOnOverflow = true;
m_pDebugName = pDebugName;
StartWriting( pData, nBytes, 0, nBits );
}
bf_write::bf_write( void *pData, int nBytes, int nBits )
{
m_bAssertOnOverflow = true;
m_pDebugName = NULL;
StartWriting( pData, nBytes, 0, nBits );
}
void bf_write::StartWriting( void *pData, int nBytes, int iStartBit, int nBits )
{
// Make sure it's dword aligned and padded.
Assert( (nBytes % 4) == 0 );
2022-02-23 11:50:30 +00:00
Assert(((uintp)pData & 3) == 0);
2020-04-22 16:56:21 +00:00
// The writing code will overrun the end of the buffer if it isn't dword aligned, so truncate to force alignment
nBytes &= ~3;
2022-02-23 11:50:30 +00:00
m_pData = (uint32*)pData;
2020-04-22 16:56:21 +00:00
m_nDataBytes = nBytes;
if ( nBits == -1 )
{
m_nDataBits = nBytes << 3;
}
else
{
Assert( nBits <= nBytes*8 );
m_nDataBits = nBits;
}
m_iCurBit = iStartBit;
m_bOverflow = false;
}
void bf_write::Reset()
{
m_iCurBit = 0;
m_bOverflow = false;
}
void bf_write::SetAssertOnOverflow( bool bAssert )
{
m_bAssertOnOverflow = bAssert;
}
const char* bf_write::GetDebugName()
{
return m_pDebugName;
}
void bf_write::SetDebugName( const char *pDebugName )
{
m_pDebugName = pDebugName;
}
void bf_write::SeekToBit( int bitPos )
{
m_iCurBit = bitPos;
}
// Sign bit comes first
void bf_write::WriteSBitLong( int data, int numbits )
{
// Force the sign-extension bit to be correct even in the case of overflow.
int nValue = data;
int nPreserveBits = ( 0x7FFFFFFF >> ( 32 - numbits ) );
int nSignExtension = ( nValue >> 31 ) & ~nPreserveBits;
nValue &= nPreserveBits;
nValue |= nSignExtension;
AssertMsg2( nValue == data, "WriteSBitLong: 0x%08x does not fit in %d bits", data, numbits );
WriteUBitLong( nValue, numbits, false );
}
void bf_write::WriteVarInt32( uint32 data )
{
// Check if align and we have room, slow path if not
if ( (m_iCurBit & 7) == 0 && (m_iCurBit + bitbuf::kMaxVarint32Bytes * 8 ) <= m_nDataBits)
{
uint8 *target = ((uint8*)m_pData) + (m_iCurBit>>3);
target[0] = static_cast<uint8>(data | 0x80);
if ( data >= (1 << 7) )
{
target[1] = static_cast<uint8>((data >> 7) | 0x80);
if ( data >= (1 << 14) )
{
target[2] = static_cast<uint8>((data >> 14) | 0x80);
if ( data >= (1 << 21) )
{
target[3] = static_cast<uint8>((data >> 21) | 0x80);
if ( data >= (1 << 28) )
{
target[4] = static_cast<uint8>(data >> 28);
m_iCurBit += 5 * 8;
return;
}
else
{
target[3] &= 0x7F;
m_iCurBit += 4 * 8;
return;
}
}
else
{
target[2] &= 0x7F;
m_iCurBit += 3 * 8;
return;
}
}
else
{
target[1] &= 0x7F;
m_iCurBit += 2 * 8;
return;
}
}
else
{
target[0] &= 0x7F;
m_iCurBit += 1 * 8;
return;
}
}
else // Slow path
{
while ( data > 0x7F )
{
WriteUBitLong( (data & 0x7F) | 0x80, 8 );
data >>= 7;
}
WriteUBitLong( data & 0x7F, 8 );
}
}
void bf_write::WriteVarInt64( uint64 data )
{
// Check if align and we have room, slow path if not
if ( (m_iCurBit & 7) == 0 && (m_iCurBit + bitbuf::kMaxVarintBytes * 8 ) <= m_nDataBits )
{
uint8 *target = ((uint8*)m_pData) + (m_iCurBit>>3);
// Splitting into 32-bit pieces gives better performance on 32-bit
// processors.
uint32 part0 = static_cast<uint32>(data );
uint32 part1 = static_cast<uint32>(data >> 28);
uint32 part2 = static_cast<uint32>(data >> 56);
int size;
// Here we can't really optimize for small numbers, since the data is
// split into three parts. Cheking for numbers < 128, for instance,
// would require three comparisons, since you'd have to make sure part1
// and part2 are zero. However, if the caller is using 64-bit integers,
// it is likely that they expect the numbers to often be very large, so
// we probably don't want to optimize for small numbers anyway. Thus,
// we end up with a hardcoded binary search tree...
if ( part2 == 0 )
{
if ( part1 == 0 )
{
if ( part0 < (1 << 14) )
{
if ( part0 < (1 << 7) )
{
size = 1; goto size1;
}
else
{
size = 2; goto size2;
}
}
else
{
if ( part0 < (1 << 21) )
{
size = 3; goto size3;
}
else
{
size = 4; goto size4;
}
}
}
else
{
if ( part1 < (1 << 14) )
{
if ( part1 < (1 << 7) )
{
size = 5; goto size5;
}
else
{
size = 6; goto size6;
}
}
else
{
if ( part1 < (1 << 21) )
{
size = 7; goto size7;
}
else
{
size = 8; goto size8;
}
}
}
}
else
{
if ( part2 < (1 << 7) )
{
size = 9; goto size9;
}
else
{
size = 10; goto size10;
}
}
AssertFatalMsg( false, "Can't get here." );
size10: target[9] = static_cast<uint8>((part2 >> 7) | 0x80);
size9 : target[8] = static_cast<uint8>((part2 ) | 0x80);
size8 : target[7] = static_cast<uint8>((part1 >> 21) | 0x80);
size7 : target[6] = static_cast<uint8>((part1 >> 14) | 0x80);
size6 : target[5] = static_cast<uint8>((part1 >> 7) | 0x80);
size5 : target[4] = static_cast<uint8>((part1 ) | 0x80);
size4 : target[3] = static_cast<uint8>((part0 >> 21) | 0x80);
size3 : target[2] = static_cast<uint8>((part0 >> 14) | 0x80);
size2 : target[1] = static_cast<uint8>((part0 >> 7) | 0x80);
size1 : target[0] = static_cast<uint8>((part0 ) | 0x80);
target[size-1] &= 0x7F;
m_iCurBit += size * 8;
}
else // slow path
{
while ( data > 0x7F )
{
WriteUBitLong( (data & 0x7F) | 0x80, 8 );
data >>= 7;
}
WriteUBitLong( data & 0x7F, 8 );
}
}
void bf_write::WriteSignedVarInt32( int32 data )
{
WriteVarInt32( bitbuf::ZigZagEncode32( data ) );
}
void bf_write::WriteSignedVarInt64( int64 data )
{
WriteVarInt64( bitbuf::ZigZagEncode64( data ) );
}
int bf_write::ByteSizeVarInt32( uint32 data )
{
int size = 1;
while ( data > 0x7F ) {
size++;
data >>= 7;
}
return size;
}
int bf_write::ByteSizeVarInt64( uint64 data )
{
int size = 1;
while ( data > 0x7F ) {
size++;
data >>= 7;
}
return size;
}
int bf_write::ByteSizeSignedVarInt32( int32 data )
{
return ByteSizeVarInt32( bitbuf::ZigZagEncode32( data ) );
}
int bf_write::ByteSizeSignedVarInt64( int64 data )
{
return ByteSizeVarInt64( bitbuf::ZigZagEncode64( data ) );
}
void bf_write::WriteBitLong(unsigned int data, int numbits, bool bSigned)
{
if(bSigned)
WriteSBitLong((int)data, numbits);
else
WriteUBitLong(data, numbits);
}
bool bf_write::WriteBits(const void *pInData, int nBits)
{
#if defined( BB_PROFILING )
VPROF( "bf_write::WriteBits" );
#endif
unsigned char *pOut = (unsigned char*)pInData;
int nBitsLeft = nBits;
// Bounds checking..
if ( (m_iCurBit+nBits) > m_nDataBits )
{
SetOverflowFlag();
CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() );
return false;
}
// Align output to dword boundary
2022-02-23 11:50:30 +00:00
while (((uintp)pOut & 3) != 0 && nBitsLeft >= 8)
2020-04-22 16:56:21 +00:00
{
WriteUBitLong( *pOut, 8, false );
++pOut;
nBitsLeft -= 8;
}
if ( IsPC() && (nBitsLeft >= 32) && (m_iCurBit & 7) == 0 )
{
// current bit is byte aligned, do block copy
int numbytes = nBitsLeft >> 3;
int numbits = numbytes << 3;
Q_memcpy( (char*)m_pData+(m_iCurBit>>3), pOut, numbytes );
pOut += numbytes;
nBitsLeft -= numbits;
m_iCurBit += numbits;
}
// X360TBD: Can't write dwords in WriteBits because they'll get swapped
if ( IsPC() && nBitsLeft >= 32 )
{
2022-02-23 11:50:30 +00:00
uint32 iBitsRight = (m_iCurBit & 31);
uint32 iBitsLeft = 32 - iBitsRight;
uint32 bitMaskLeft = g_BitWriteMasks[iBitsRight][32];
uint32 bitMaskRight = g_BitWriteMasks[0][iBitsRight];
2020-04-22 16:56:21 +00:00
2022-02-23 11:50:30 +00:00
uint32 *pData = &m_pData[m_iCurBit>>5];
2020-04-22 16:56:21 +00:00
// Read dwords.
while(nBitsLeft >= 32)
{
2022-02-23 11:50:30 +00:00
uint32 curData = *(uint32*)pOut;
pOut += sizeof(uint32);
2020-04-22 16:56:21 +00:00
*pData &= bitMaskLeft;
*pData |= curData << iBitsRight;
pData++;
if ( iBitsLeft < 32 )
{
curData >>= iBitsLeft;
*pData &= bitMaskRight;
*pData |= curData;
}
nBitsLeft -= 32;
m_iCurBit += 32;
}
}
// write remaining bytes
while ( nBitsLeft >= 8 )
{
WriteUBitLong( *pOut, 8, false );
++pOut;
nBitsLeft -= 8;
}
// write remaining bits
if ( nBitsLeft )
{
WriteUBitLong( *pOut, nBitsLeft, false );
}
return !IsOverflowed();
}
bool bf_write::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 bf_write::WriteBitAngle( float fAngle, int numbits )
{
int d;
unsigned int mask;
unsigned int shift;
shift = BitForBitnum(numbits);
mask = shift - 1;
d = (int)( (fAngle / 360.0) * shift );
d &= mask;
WriteUBitLong((unsigned int)d, numbits);
}
void bf_write::WriteBitCoordMP( const float f, bool bIntegral, bool bLowPrecision )
{
#if defined( BB_PROFILING )
VPROF( "bf_write::WriteBitCoordMP" );
#endif
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 );
unsigned int bits, numbits;
if ( bIntegral )
{
// Integer encoding: in-bounds bit, nonzero bit, optional sign bit + integer value bits
if ( intval )
{
// Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1]
--intval;
bits = intval * 8 + signbit * 4 + 2 + bInBounds;
numbits = 3 + (bInBounds ? COORD_INTEGER_BITS_MP : COORD_INTEGER_BITS);
}
else
{
bits = bInBounds;
numbits = 2;
}
}
else
{
// Float encoding: in-bounds bit, integer bit, sign bit, fraction value bits, optional integer value bits
if ( intval )
{
// Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1]
--intval;
bits = intval * 8 + signbit * 4 + 2 + bInBounds;
bits += bInBounds ? (fractval << (3+COORD_INTEGER_BITS_MP)) : (fractval << (3+COORD_INTEGER_BITS));
numbits = 3 + (bInBounds ? COORD_INTEGER_BITS_MP : COORD_INTEGER_BITS)
+ (bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS);
}
else
{
bits = fractval * 8 + signbit * 4 + 0 + bInBounds;
numbits = 3 + (bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS);
}
}
WriteUBitLong( bits, numbits );
}
void bf_write::WriteBitCoord (const float f)
{
#if defined( BB_PROFILING )
VPROF( "bf_write::WriteBitCoord" );
#endif
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 bf_write::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 bf_write::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 bf_write::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 bf_write::WriteBitAngles( const QAngle& fa )
{
// FIXME:
Vector tmp( fa.x, fa.y, fa.z );
WriteBitVec3Coord( tmp );
}
void bf_write::WriteChar(int val)
{
WriteSBitLong(val, sizeof(char) << 3);
}
void bf_write::WriteByte(int val)
{
WriteUBitLong(val, sizeof(unsigned char) << 3);
}
void bf_write::WriteShort(int val)
{
WriteSBitLong(val, sizeof(short) << 3);
}
void bf_write::WriteWord(int val)
{
WriteUBitLong(val, sizeof(unsigned short) << 3);
}
2022-02-23 11:50:30 +00:00
void bf_write::WriteLong(int32 val)
2020-04-22 16:56:21 +00:00
{
2022-02-23 11:50:30 +00:00
WriteSBitLong(val, sizeof(int32) << 3);
2020-04-22 16:56:21 +00:00
}
void bf_write::WriteLongLong(int64 val)
{
uint *pLongs = (uint*)&val;
// Insert the two DWORDS according to network endian
const short endianIndex = 0x0100;
byte *idx = (byte*)&endianIndex;
2022-02-23 11:50:30 +00:00
WriteUBitLong(pLongs[*idx++], sizeof(int32) << 3);
WriteUBitLong(pLongs[*idx], sizeof(int32) << 3);
2020-04-22 16:56:21 +00:00
}
void bf_write::WriteFloat(float val)
{
// Pre-swap the float, since WriteBits writes raw data
LittleFloat( &val, &val );
WriteBits(&val, sizeof(val) << 3);
}
bool bf_write::WriteBytes( const void *pBuf, int nBytes )
{
return WriteBits(pBuf, nBytes << 3);
}
bool bf_write::WriteString(const char *pStr)
{
if(pStr)
{
do
{
WriteChar( *pStr );
++pStr;
} while( *(pStr-1) != 0 );
}
else
{
WriteChar( 0 );
}
return !IsOverflowed();
}
// ---------------------------------------------------------------------------------------- //
// bf_read
// ---------------------------------------------------------------------------------------- //
bf_read::bf_read()
{
m_pData = NULL;
m_nDataBytes = 0;
m_nDataBits = -1; // set to -1 so we overflow on any operation
m_iCurBit = 0;
m_bOverflow = false;
m_bAssertOnOverflow = true;
m_pDebugName = NULL;
}
bf_read::bf_read( const void *pData, int nBytes, int nBits )
{
m_bAssertOnOverflow = true;
StartReading( pData, nBytes, 0, nBits );
}
bf_read::bf_read( const char *pDebugName, const void *pData, int nBytes, int nBits )
{
m_bAssertOnOverflow = true;
m_pDebugName = pDebugName;
StartReading( pData, nBytes, 0, nBits );
}
void bf_read::StartReading( const void *pData, int nBytes, int iStartBit, int nBits )
{
// Make sure we're dword aligned.
Assert(((size_t)pData & 3) == 0);
m_pData = (unsigned char*)pData;
m_nDataBytes = nBytes;
if ( nBits == -1 )
{
m_nDataBits = m_nDataBytes << 3;
}
else
{
Assert( nBits <= nBytes*8 );
m_nDataBits = nBits;
}
m_iCurBit = iStartBit;
m_bOverflow = false;
}
void bf_read::Reset()
{
m_iCurBit = 0;
m_bOverflow = false;
}
void bf_read::SetAssertOnOverflow( bool bAssert )
{
m_bAssertOnOverflow = bAssert;
}
void bf_read::SetDebugName( const char *pName )
{
m_pDebugName = pName;
}
2021-04-28 15:24:16 +00:00
void bf_read::SetOverflowFlag() RESTRICT
2020-04-22 16:56:21 +00:00
{
if ( m_bAssertOnOverflow )
{
Assert( false );
}
m_bOverflow = true;
}
unsigned int bf_read::CheckReadUBitLong(int numbits)
{
// Ok, just read bits out.
int i, nBitValue;
unsigned int r = 0;
for(i=0; i < numbits; i++)
{
nBitValue = ReadOneBitNoCheck();
r |= nBitValue << i;
}
m_iCurBit -= numbits;
return r;
}
void bf_read::ReadBits(void *pOutData, int nBits)
{
#if defined( BB_PROFILING )
VPROF( "bf_read::ReadBits" );
#endif
unsigned char *pOut = (unsigned char*)pOutData;
int nBitsLeft = nBits;
// align output to dword boundary
while( ((size_t)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 )
{
2022-02-23 11:50:30 +00:00
*((uint32*)pOut) = ReadUBitLong(32);
pOut += sizeof(uint32);
2020-04-22 16:56:21 +00:00
nBitsLeft -= 32;
}
}
// read remaining bytes
while ( nBitsLeft >= 8 )
{
*pOut = ReadUBitLong(8);
++pOut;
nBitsLeft -= 8;
}
// read remaining bits
if ( nBitsLeft )
{
*pOut = ReadUBitLong(nBitsLeft);
}
}
int bf_read::ReadBitsClamped_ptr(void *pOutData, size_t outSizeBytes, size_t nBits)
{
size_t outSizeBits = outSizeBytes * 8;
size_t readSizeBits = nBits;
int skippedBits = 0;
if ( readSizeBits > outSizeBits )
{
// Should we print a message when we clamp the data being read? Only
// in debug builds I think.
AssertMsg( 0, "Oversized network packet received, and clamped." );
readSizeBits = outSizeBits;
skippedBits = (int)( nBits - outSizeBits );
// What should we do in this case, which should only happen if nBits
// is negative for some reason?
//if ( skippedBits < 0 )
// return 0;
}
ReadBits( pOutData, readSizeBits );
SeekRelative( skippedBits );
// Return the number of bits actually read.
return (int)readSizeBits;
}
float bf_read::ReadBitAngle( int numbits )
{
float fReturn;
int i;
float shift;
shift = (float)( BitForBitnum(numbits) );
i = ReadUBitLong( numbits );
fReturn = (float)i * (360.0 / shift);
return fReturn;
}
unsigned int bf_read::PeekUBitLong( int numbits )
{
unsigned int r;
int i, nBitValue;
#ifdef BIT_VERBOSE
int nShifts = numbits;
#endif
bf_read savebf;
savebf = *this; // Save current state info
r = 0;
for(i=0; i < numbits; i++)
{
nBitValue = ReadOneBit();
// Append to current stream
if ( nBitValue )
{
r |= BitForBitnum(i);
}
}
*this = savebf;
#ifdef BIT_VERBOSE
Con_Printf( "PeekBitLong: %i %i\n", nShifts, (unsigned int)r );
#endif
return r;
}
2021-04-28 15:24:16 +00:00
unsigned int bf_read::ReadUBitLongNoInline( int numbits ) RESTRICT
2020-04-22 16:56:21 +00:00
{
return ReadUBitLong( numbits );
}
unsigned int bf_read::ReadUBitVarInternal( int encodingType )
{
m_iCurBit -= 4;
// int bits = { 4, 8, 12, 32 }[ encodingType ];
int bits = 4 + encodingType*4 + (((2 - encodingType) >> 31) & 16);
return ReadUBitLong( bits );
}
// Append numbits least significant bits from data to the current bit stream
int bf_read::ReadSBitLong( int numbits )
{
unsigned int r = ReadUBitLong(numbits);
unsigned int s = 1 << (numbits-1);
if (r >= s)
{
// sign-extend by removing sign bit and then subtracting sign bit again
r = r - s - s;
}
return r;
}
uint32 bf_read::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 bf_read::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;
}
int32 bf_read::ReadSignedVarInt32()
{
uint32 value = ReadVarInt32();
return bitbuf::ZigZagDecode32( value );
}
int64 bf_read::ReadSignedVarInt64()
{
uint32 value = ReadVarInt64();
return bitbuf::ZigZagDecode64( value );
}
unsigned int bf_read::ReadBitLong(int numbits, bool bSigned)
{
if(bSigned)
return (unsigned int)ReadSBitLong(numbits);
else
return ReadUBitLong(numbits);
}
// Basic Coordinate Routines (these contain bit-field size AND fixed point scaling constants)
float bf_read::ReadBitCoord (void)
{
#if defined( BB_PROFILING )
VPROF( "bf_read::ReadBitCoord" );
#endif
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 bf_read::ReadBitCoordMP( bool bIntegral, bool bLowPrecision )
{
#if defined( BB_PROFILING )
VPROF( "bf_read::ReadBitCoordMP" );
#endif
// BitCoordMP float encoding: inbounds bit, integer bit, sign bit, optional int bits, float bits
// BitCoordMP integer encoding: inbounds bit, integer bit, optional sign bit, optional int bits.
// int bits are always encoded as (value - 1) since zero is handled by the integer bit
// With integer-only encoding, the presence of the third bit depends on the second
int flags = ReadUBitLong(3 - bIntegral);
enum { INBOUNDS=1, INTVAL=2, SIGN=4 };
if ( bIntegral )
{
if ( flags & INTVAL )
{
// Read the third bit and the integer portion together at once
unsigned int bits = ReadUBitLong( (flags & INBOUNDS) ? COORD_INTEGER_BITS_MP+1 : COORD_INTEGER_BITS+1 );
// Remap from [0,N] to [1,N+1]
int intval = (bits >> 1) + 1;
return (bits & 1) ? -intval : intval;
}
return 0.f;
}
static const float mul_table[4] =
{
1.f/(1<<COORD_FRACTIONAL_BITS),
-1.f/(1<<COORD_FRACTIONAL_BITS),
1.f/(1<<COORD_FRACTIONAL_BITS_MP_LOWPRECISION),
-1.f/(1<<COORD_FRACTIONAL_BITS_MP_LOWPRECISION)
};
//equivalent to: float multiply = mul_table[ ((flags & SIGN) ? 1 : 0) + bLowPrecision*2 ];
float multiply = *(float*)((uintptr_t)&mul_table[0] + (flags & 4) + bLowPrecision*8);
static const unsigned char numbits_table[8] =
{
COORD_FRACTIONAL_BITS,
COORD_FRACTIONAL_BITS,
COORD_FRACTIONAL_BITS + COORD_INTEGER_BITS,
COORD_FRACTIONAL_BITS + COORD_INTEGER_BITS_MP,
COORD_FRACTIONAL_BITS_MP_LOWPRECISION,
COORD_FRACTIONAL_BITS_MP_LOWPRECISION,
COORD_FRACTIONAL_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS,
COORD_FRACTIONAL_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS_MP
};
unsigned int bits = ReadUBitLong( numbits_table[ (flags & (INBOUNDS|INTVAL)) + bLowPrecision*4 ] );
if ( flags & INTVAL )
{
// Shuffle the bits to remap the integer portion from [0,N] to [1,N+1]
// and then paste in front of the fractional parts so we only need one
// int-to-float conversion.
uint fracbitsMP = bits >> COORD_INTEGER_BITS_MP;
uint fracbits = bits >> COORD_INTEGER_BITS;
uint intmaskMP = ((1<<COORD_INTEGER_BITS_MP)-1);
uint intmask = ((1<<COORD_INTEGER_BITS)-1);
uint selectNotMP = (flags & INBOUNDS) - 1;
fracbits -= fracbitsMP;
fracbits &= selectNotMP;
fracbits += fracbitsMP;
intmask -= intmaskMP;
intmask &= selectNotMP;
intmask += intmaskMP;
uint intpart = (bits & intmask) + 1;
uint intbitsLow = intpart << COORD_FRACTIONAL_BITS_MP_LOWPRECISION;
uint intbits = intpart << COORD_FRACTIONAL_BITS;
uint selectNotLow = (uint)bLowPrecision - 1;
intbits -= intbitsLow;
intbits &= selectNotLow;
intbits += intbitsLow;
bits = fracbits | intbits;
}
return (int)bits * multiply;
}
unsigned int bf_read::ReadBitCoordBits (void)
{
#if defined( BB_PROFILING )
VPROF( "bf_read::ReadBitCoordBits" );
#endif
unsigned int flags = ReadUBitLong(2);
if ( flags == 0 )
return 0;
static const int numbits_table[3] =
{
COORD_INTEGER_BITS + 1,
COORD_FRACTIONAL_BITS + 1,
COORD_INTEGER_BITS + COORD_FRACTIONAL_BITS + 1
};
return ReadUBitLong( numbits_table[ flags-1 ] ) * 4 + flags;
}
unsigned int bf_read::ReadBitCoordMPBits( bool bIntegral, bool bLowPrecision )
{
#if defined( BB_PROFILING )
VPROF( "bf_read::ReadBitCoordMPBits" );
#endif
unsigned int flags = ReadUBitLong(2);
enum { INBOUNDS=1, INTVAL=2 };
int numbits = 0;
if ( bIntegral )
{
if ( flags & INTVAL )
{
numbits = (flags & INBOUNDS) ? (1 + COORD_INTEGER_BITS_MP) : (1 + COORD_INTEGER_BITS);
}
else
{
return flags; // no extra bits
}
}
else
{
static const unsigned char numbits_table[8] =
{
1 + COORD_FRACTIONAL_BITS,
1 + COORD_FRACTIONAL_BITS,
1 + COORD_FRACTIONAL_BITS + COORD_INTEGER_BITS,
1 + COORD_FRACTIONAL_BITS + COORD_INTEGER_BITS_MP,
1 + COORD_FRACTIONAL_BITS_MP_LOWPRECISION,
1 + COORD_FRACTIONAL_BITS_MP_LOWPRECISION,
1 + COORD_FRACTIONAL_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS,
1 + COORD_FRACTIONAL_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS_MP
};
numbits = numbits_table[ flags + bLowPrecision*4 ];
}
return flags + ReadUBitLong(numbits)*4;
}
void bf_read::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 bf_read::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 bf_read::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 bf_read::ReadBitAngles( QAngle& fa )
{
Vector tmp;
ReadBitVec3Coord( tmp );
fa.Init( tmp.x, tmp.y, tmp.z );
}
int64 bf_read::ReadLongLong()
{
int64 retval;
uint *pLongs = (uint*)&retval;
// Read the two DWORDs according to network endian
const short endianIndex = 0x0100;
byte *idx = (byte*)&endianIndex;
2022-02-23 11:50:30 +00:00
pLongs[*idx++] = ReadUBitLong(sizeof(int32) << 3);
pLongs[*idx] = ReadUBitLong(sizeof(int32) << 3);
2020-04-22 16:56:21 +00:00
return retval;
}
float bf_read::ReadFloat()
{
float ret;
Assert( sizeof(ret) == 4 );
ReadBits(&ret, 32);
// Swap the float, since ReadBits reads raw data
LittleFloat( &ret, &ret );
return ret;
}
bool bf_read::ReadBytes(void *pOut, int nBytes)
{
ReadBits(pOut, nBytes << 3);
return !IsOverflowed();
}
bool bf_read::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;
}
char* bf_read::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;
}
void bf_read::ExciseBits( int startbit, int bitstoremove )
{
int endbit = startbit + bitstoremove;
int remaining_to_end = m_nDataBits - endbit;
bf_write temp;
temp.StartWriting( (void *)m_pData, m_nDataBits << 3, startbit );
Seek( endbit );
for ( int i = 0; i < remaining_to_end; i++ )
{
temp.WriteOneBit( ReadOneBit() );
}
Seek( startbit );
m_nDataBits -= bitstoremove;
m_nDataBytes = m_nDataBits >> 3;
}
int bf_read::CompareBitsAt( int offset, bf_read * RESTRICT other, int otherOffset, int numbits ) RESTRICT
{
2022-02-23 11:50:30 +00:00
extern uint32 g_ExtraMasks[33];
2020-04-22 16:56:21 +00:00
if ( numbits == 0 )
return 0;
int overflow1 = offset + numbits > m_nDataBits;
int overflow2 = otherOffset + numbits > other->m_nDataBits;
int x = overflow1 | overflow2;
if ( x != 0 )
return x;
unsigned int iStartBit1 = offset & 31u;
unsigned int iStartBit2 = otherOffset & 31u;
2022-02-23 11:50:30 +00:00
uint32 *pData1 = (uint32*)m_pData + (offset >> 5);
uint32 *pData2 = (uint32*)other->m_pData + (otherOffset >> 5);
uint32 *pData1End = pData1 + ((offset + numbits - 1) >> 5);
uint32 *pData2End = pData2 + ((otherOffset + numbits - 1) >> 5);
2020-04-22 16:56:21 +00:00
while ( numbits > 32 )
{
2022-02-23 11:50:30 +00:00
x = LoadLittleDWord( (uint32*)pData1, 0 ) >> iStartBit1;
x ^= LoadLittleDWord( (uint32*)pData1, 1 ) << (32 - iStartBit1);
x ^= LoadLittleDWord( (uint32*)pData2, 0 ) >> iStartBit2;
x ^= LoadLittleDWord( (uint32*)pData2, 1 ) << (32 - iStartBit2);
2020-04-22 16:56:21 +00:00
if ( x != 0 )
{
return x;
}
++pData1;
++pData2;
numbits -= 32;
}
2022-02-23 11:50:30 +00:00
x = LoadLittleDWord( (uint32*)pData1, 0 ) >> iStartBit1;
x ^= LoadLittleDWord( (uint32*)pData1End, 0 ) << (32 - iStartBit1);
x ^= LoadLittleDWord( (uint32*)pData2, 0 ) >> iStartBit2;
x ^= LoadLittleDWord( (uint32*)pData2End, 0 ) << (32 - iStartBit2);
2020-04-22 16:56:21 +00:00
return x & g_ExtraMasks[ numbits ];
}