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.
809 lines
22 KiB
809 lines
22 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
|
|
// NOTE: bf_read is guaranteed to return zeros if it overflows. |
|
|
|
#ifndef BITBUF_H |
|
#define BITBUF_H |
|
|
|
#ifdef _WIN32 |
|
#pragma once |
|
#endif |
|
|
|
|
|
#include "mathlib/mathlib.h" |
|
#include "mathlib/vector.h" |
|
#include "basetypes.h" |
|
#include "tier0/dbg.h" |
|
#include "strtools.h" |
|
|
|
#if _DEBUG |
|
#define BITBUF_INLINE inline |
|
#else |
|
#define BITBUF_INLINE FORCEINLINE |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Forward declarations. |
|
//----------------------------------------------------------------------------- |
|
|
|
class Vector; |
|
class QAngle; |
|
|
|
//----------------------------------------------------------------------------- |
|
// You can define a handler function that will be called in case of |
|
// out-of-range values and overruns here. |
|
// |
|
// NOTE: the handler is only called in debug mode. |
|
// |
|
// Call SetBitBufErrorHandler to install a handler. |
|
//----------------------------------------------------------------------------- |
|
|
|
typedef enum |
|
{ |
|
BITBUFERROR_VALUE_OUT_OF_RANGE=0, // Tried to write a value with too few bits. |
|
BITBUFERROR_BUFFER_OVERRUN, // Was about to overrun a buffer. |
|
|
|
BITBUFERROR_NUM_ERRORS |
|
} BitBufErrorType; |
|
|
|
|
|
typedef void (*BitBufErrorHandler)( BitBufErrorType errorType, const char *pDebugName ); |
|
|
|
|
|
#if defined( _DEBUG ) |
|
extern void InternalBitBufErrorHandler( BitBufErrorType errorType, const char *pDebugName ); |
|
#define CallErrorHandler( errorType, pDebugName ) InternalBitBufErrorHandler( errorType, pDebugName ); |
|
#else |
|
#define CallErrorHandler( errorType, pDebugName ) |
|
#endif |
|
|
|
|
|
// Use this to install the error handler. Call with NULL to uninstall your error handler. |
|
void SetBitBufErrorHandler( BitBufErrorHandler fn ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Helpers. |
|
//----------------------------------------------------------------------------- |
|
|
|
inline int BitByte( int bits ) |
|
{ |
|
// return PAD_NUMBER( bits, 8 ) >> 3; |
|
return (bits + 7) >> 3; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// namespaced helpers |
|
//----------------------------------------------------------------------------- |
|
namespace bitbuf |
|
{ |
|
// ZigZag Transform: Encodes signed integers so that they can be |
|
// effectively used with varint encoding. |
|
// |
|
// varint operates on unsigned integers, encoding smaller numbers into |
|
// fewer bytes. If you try to use it on a signed integer, it will treat |
|
// this number as a very large unsigned integer, which means that even |
|
// small signed numbers like -1 will take the maximum number of bytes |
|
// (10) to encode. ZigZagEncode() maps signed integers to unsigned |
|
// in such a way that those with a small absolute value will have smaller |
|
// encoded values, making them appropriate for encoding using varint. |
|
// |
|
// int32 -> uint32 |
|
// ------------------------- |
|
// 0 -> 0 |
|
// -1 -> 1 |
|
// 1 -> 2 |
|
// -2 -> 3 |
|
// ... -> ... |
|
// 2147483647 -> 4294967294 |
|
// -2147483648 -> 4294967295 |
|
// |
|
// >> encode >> |
|
// << decode << |
|
|
|
inline uint32 ZigZagEncode32(int32 n) |
|
{ |
|
// Note: the right-shift must be arithmetic |
|
return(n << 1) ^ (n >> 31); |
|
} |
|
|
|
inline int32 ZigZagDecode32(uint32 n) |
|
{ |
|
return(n >> 1) ^ -static_cast<int32>(n & 1); |
|
} |
|
|
|
inline uint64 ZigZagEncode64(int64 n) |
|
{ |
|
// Note: the right-shift must be arithmetic |
|
return(n << 1) ^ (n >> 63); |
|
} |
|
|
|
inline int64 ZigZagDecode64(uint64 n) |
|
{ |
|
return(n >> 1) ^ -static_cast<int64>(n & 1); |
|
} |
|
|
|
const int kMaxVarintBytes = 10; |
|
const int kMaxVarint32Bytes = 5; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Used for serialization |
|
//----------------------------------------------------------------------------- |
|
|
|
class bf_write |
|
{ |
|
public: |
|
bf_write(); |
|
|
|
// nMaxBits can be used as the number of bits in the buffer. |
|
// It must be <= nBytes*8. If you leave it at -1, then it's set to nBytes * 8. |
|
bf_write( void *pData, int nBytes, int nMaxBits = -1 ); |
|
bf_write( const char *pDebugName, void *pData, int nBytes, int nMaxBits = -1 ); |
|
|
|
// Start writing to the specified buffer. |
|
// nMaxBits can be used as the number of bits in the buffer. |
|
// It must be <= nBytes*8. If you leave it at -1, then it's set to nBytes * 8. |
|
void StartWriting( void *pData, int nBytes, int iStartBit = 0, int nMaxBits = -1 ); |
|
|
|
// Restart buffer writing. |
|
void Reset(); |
|
|
|
// Get the base pointer. |
|
unsigned char* GetBasePointer() { return (unsigned char*) m_pData; } |
|
|
|
// Enable or disable assertion on overflow. 99% of the time, it's a bug that we need to catch, |
|
// but there may be the occasional buffer that is allowed to overflow gracefully. |
|
void SetAssertOnOverflow( bool bAssert ); |
|
|
|
// This can be set to assign a name that gets output if the buffer overflows. |
|
const char* GetDebugName(); |
|
void SetDebugName( const char *pDebugName ); |
|
|
|
|
|
// Seek to a specific position. |
|
public: |
|
|
|
void SeekToBit( int bitPos ); |
|
|
|
|
|
// Bit functions. |
|
public: |
|
|
|
void WriteOneBit(int nValue); |
|
void WriteOneBitNoCheck(int nValue); |
|
void WriteOneBitAt( int iBit, int nValue ); |
|
|
|
// Write signed or unsigned. Range is only checked in debug. |
|
void WriteUBitLong( unsigned int data, int numbits, bool bCheckRange=true ) RESTRICT; |
|
void WriteSBitLong( int data, int numbits ); |
|
|
|
// Tell it whether or not the data is unsigned. If it's signed, |
|
// cast to unsigned before passing in (it will cast back inside). |
|
void WriteBitLong(unsigned int data, int numbits, bool bSigned); |
|
|
|
// Write a list of bits in. |
|
bool WriteBits(const void *pIn, int nBits); |
|
|
|
// writes an unsigned integer with variable bit length |
|
void WriteUBitVar( unsigned int data ); |
|
|
|
// writes a varint encoded integer |
|
void WriteVarInt32( uint32 data ); |
|
void WriteVarInt64( uint64 data ); |
|
void WriteSignedVarInt32( int32 data ); |
|
void WriteSignedVarInt64( int64 data ); |
|
int ByteSizeVarInt32( uint32 data ); |
|
int ByteSizeVarInt64( uint64 data ); |
|
int ByteSizeSignedVarInt32( int32 data ); |
|
int ByteSizeSignedVarInt64( int64 data ); |
|
|
|
// Copy the bits straight out of pIn. This seeks pIn forward by nBits. |
|
// Returns an error if this buffer or the read buffer overflows. |
|
bool WriteBitsFromBuffer( class bf_read *pIn, int nBits ); |
|
|
|
void WriteBitAngle( float fAngle, int numbits ); |
|
void WriteBitCoord (const float f); |
|
void WriteBitCoordMP( const float f, bool bIntegral, bool bLowPrecision ); |
|
void WriteBitFloat(float val); |
|
void WriteBitVec3Coord( const Vector& fa ); |
|
void WriteBitNormal( float f ); |
|
void WriteBitVec3Normal( const Vector& fa ); |
|
void WriteBitAngles( const QAngle& fa ); |
|
|
|
|
|
// Byte functions. |
|
public: |
|
|
|
void WriteChar(int val); |
|
void WriteByte(int val); |
|
void WriteShort(int val); |
|
void WriteWord(int val); |
|
void WriteLong(int32 val); |
|
void WriteLongLong(int64 val); |
|
void WriteFloat(float val); |
|
bool WriteBytes( const void *pBuf, int nBytes ); |
|
|
|
// Returns false if it overflows the buffer. |
|
bool WriteString(const char *pStr); |
|
|
|
|
|
// Status. |
|
public: |
|
|
|
// How many bytes are filled in? |
|
int GetNumBytesWritten() const; |
|
int GetNumBitsWritten() const; |
|
int GetMaxNumBits(); |
|
int GetNumBitsLeft() RESTRICT; |
|
int GetNumBytesLeft(); |
|
unsigned char* GetData(); |
|
const unsigned char* GetData() const; |
|
|
|
// Has the buffer overflowed? |
|
bool CheckForOverflow(int nBits); |
|
inline bool IsOverflowed() const {return m_bOverflow;} |
|
|
|
void SetOverflowFlag() RESTRICT; |
|
|
|
|
|
public: |
|
// The current buffer. |
|
uint32* RESTRICT m_pData; |
|
int m_nDataBytes; |
|
int m_nDataBits; |
|
|
|
// Where we are in the buffer. |
|
int m_iCurBit; |
|
|
|
private: |
|
|
|
// Errors? |
|
bool m_bOverflow; |
|
|
|
bool m_bAssertOnOverflow; |
|
const char *m_pDebugName; |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Inlined methods |
|
//----------------------------------------------------------------------------- |
|
|
|
// How many bytes are filled in? |
|
inline int bf_write::GetNumBytesWritten() const |
|
{ |
|
return BitByte(m_iCurBit); |
|
} |
|
|
|
inline int bf_write::GetNumBitsWritten() const |
|
{ |
|
return m_iCurBit; |
|
} |
|
|
|
inline int bf_write::GetMaxNumBits() |
|
{ |
|
return m_nDataBits; |
|
} |
|
|
|
inline int bf_write::GetNumBitsLeft() RESTRICT |
|
{ |
|
return m_nDataBits - m_iCurBit; |
|
} |
|
|
|
inline int bf_write::GetNumBytesLeft() |
|
{ |
|
return GetNumBitsLeft() >> 3; |
|
} |
|
|
|
inline unsigned char* bf_write::GetData() |
|
{ |
|
return (unsigned char*) m_pData; |
|
} |
|
|
|
inline const unsigned char* bf_write::GetData() const |
|
{ |
|
return (unsigned char*) m_pData; |
|
} |
|
|
|
BITBUF_INLINE bool bf_write::CheckForOverflow(int nBits) |
|
{ |
|
if ( m_iCurBit + nBits > m_nDataBits ) |
|
{ |
|
SetOverflowFlag(); |
|
CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); |
|
} |
|
|
|
return m_bOverflow; |
|
} |
|
|
|
BITBUF_INLINE void bf_write::SetOverflowFlag() RESTRICT |
|
{ |
|
#ifdef DBGFLAG_ASSERT |
|
if ( m_bAssertOnOverflow ) |
|
{ |
|
Assert( false ); |
|
} |
|
#endif |
|
m_bOverflow = true; |
|
} |
|
|
|
BITBUF_INLINE void bf_write::WriteOneBitNoCheck(int nValue) |
|
{ |
|
#if __i386__ |
|
if(nValue) |
|
m_pData[m_iCurBit >> 5] |= 1u << (m_iCurBit & 31); |
|
else |
|
m_pData[m_iCurBit >> 5] &= ~(1u << (m_iCurBit & 31)); |
|
#else |
|
extern uint32 g_LittleBits[32]; |
|
if(nValue) |
|
m_pData[m_iCurBit >> 5] |= g_LittleBits[m_iCurBit & 31]; |
|
else |
|
m_pData[m_iCurBit >> 5] &= ~g_LittleBits[m_iCurBit & 31]; |
|
#endif |
|
|
|
++m_iCurBit; |
|
} |
|
|
|
inline void bf_write::WriteOneBit(int nValue) |
|
{ |
|
if( m_iCurBit >= m_nDataBits ) |
|
{ |
|
SetOverflowFlag(); |
|
CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); |
|
return; |
|
} |
|
WriteOneBitNoCheck( nValue ); |
|
} |
|
|
|
|
|
inline void bf_write::WriteOneBitAt( int iBit, int nValue ) |
|
{ |
|
if( iBit >= m_nDataBits ) |
|
{ |
|
SetOverflowFlag(); |
|
CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); |
|
return; |
|
} |
|
|
|
#if __i386__ |
|
if(nValue) |
|
m_pData[iBit >> 5] |= 1u << (iBit & 31); |
|
else |
|
m_pData[iBit >> 5] &= ~(1u << (iBit & 31)); |
|
#else |
|
extern uint32 g_LittleBits[32]; |
|
if(nValue) |
|
m_pData[iBit >> 5] |= g_LittleBits[iBit & 31]; |
|
else |
|
m_pData[iBit >> 5] &= ~g_LittleBits[iBit & 31]; |
|
#endif |
|
} |
|
|
|
BITBUF_INLINE void bf_write::WriteUBitLong( unsigned int curData, int numbits, bool bCheckRange ) RESTRICT |
|
{ |
|
#ifdef _DEBUG |
|
// Make sure it doesn't overflow. |
|
if ( bCheckRange && numbits < 32 ) |
|
{ |
|
if ( curData >= (uint32)(1 << numbits) ) |
|
{ |
|
CallErrorHandler( BITBUFERROR_VALUE_OUT_OF_RANGE, GetDebugName() ); |
|
} |
|
} |
|
Assert( numbits >= 0 && numbits <= 32 ); |
|
#endif |
|
|
|
if ( GetNumBitsLeft() < numbits ) |
|
{ |
|
m_iCurBit = m_nDataBits; |
|
SetOverflowFlag(); |
|
CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); |
|
return; |
|
} |
|
|
|
int iCurBitMasked = m_iCurBit & 31; |
|
int iDWord = m_iCurBit >> 5; |
|
m_iCurBit += numbits; |
|
|
|
// Mask in a dword. |
|
Assert( (iDWord*4 + sizeof(int32)) <= (unsigned int)m_nDataBytes ); |
|
uint32 * RESTRICT pOut = &m_pData[iDWord]; |
|
|
|
// Rotate data into dword alignment |
|
curData = (curData << iCurBitMasked) | (curData >> (32 - iCurBitMasked)); |
|
|
|
// Calculate bitmasks for first and second word |
|
unsigned int temp = 1 << (numbits-1); |
|
unsigned int mask1 = (temp*2-1) << iCurBitMasked; |
|
unsigned int mask2 = (temp-1) >> (31 - iCurBitMasked); |
|
|
|
// Only look beyond current word if necessary (avoid access violation) |
|
int i = mask2 & 1; |
|
uint32 dword1 = LoadLittleDWord( pOut, 0 ); |
|
uint32 dword2 = LoadLittleDWord( pOut, i ); |
|
|
|
// Drop bits into place |
|
dword1 ^= ( mask1 & ( curData ^ dword1 ) ); |
|
dword2 ^= ( mask2 & ( curData ^ dword2 ) ); |
|
|
|
// Note reversed order of writes so that dword1 wins if mask2 == 0 && i == 0 |
|
StoreLittleDWord( pOut, i, dword2 ); |
|
StoreLittleDWord( pOut, 0, dword1 ); |
|
} |
|
|
|
// writes an unsigned integer with variable bit length |
|
BITBUF_INLINE void bf_write::WriteUBitVar( unsigned int data ) |
|
{ |
|
/* Reference: |
|
if ( data < 0x10u ) |
|
WriteUBitLong( 0, 2 ), WriteUBitLong( data, 4 ); |
|
else if ( data < 0x100u ) |
|
WriteUBitLong( 1, 2 ), WriteUBitLong( data, 8 ); |
|
else if ( data < 0x1000u ) |
|
WriteUBitLong( 2, 2 ), WriteUBitLong( data, 12 ); |
|
else |
|
WriteUBitLong( 3, 2 ), WriteUBitLong( data, 32 ); |
|
*/ |
|
// a < b ? -1 : 0 translates into a CMP, SBB instruction pair |
|
// with no flow control. should also be branchless on consoles. |
|
int n = (data < 0x10u ? -1 : 0) + (data < 0x100u ? -1 : 0) + (data < 0x1000u ? -1 : 0); |
|
WriteUBitLong( data*4 + n + 3, 6 + n*4 + 12 ); |
|
if ( data >= 0x1000u ) |
|
{ |
|
WriteUBitLong( data >> 16, 16 ); |
|
} |
|
} |
|
|
|
// write raw IEEE float bits in little endian form |
|
BITBUF_INLINE void bf_write::WriteBitFloat(float val) |
|
{ |
|
int32 intVal; |
|
|
|
Assert(sizeof(int32) == sizeof(float)); |
|
Assert(sizeof(float) == 4); |
|
|
|
Q_memcpy( &intVal, &val, sizeof(intVal)); |
|
WriteUBitLong( intVal, 32 ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// This is useful if you just want a buffer to write into on the stack. |
|
//----------------------------------------------------------------------------- |
|
|
|
template<int SIZE> |
|
class old_bf_write_static : public bf_write |
|
{ |
|
public: |
|
inline old_bf_write_static() : bf_write(m_StaticData, SIZE) {} |
|
|
|
char m_StaticData[SIZE]; |
|
}; |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used for unserialization |
|
//----------------------------------------------------------------------------- |
|
|
|
class bf_read |
|
{ |
|
public: |
|
bf_read(); |
|
|
|
// nMaxBits can be used as the number of bits in the buffer. |
|
// It must be <= nBytes*8. If you leave it at -1, then it's set to nBytes * 8. |
|
bf_read( const void *pData, int nBytes, int nBits = -1 ); |
|
bf_read( const char *pDebugName, const void *pData, int nBytes, int nBits = -1 ); |
|
|
|
// Start reading from the specified buffer. |
|
// pData's start address must be dword-aligned. |
|
// nMaxBits can be used as the number of bits in the buffer. |
|
// It must be <= nBytes*8. If you leave it at -1, then it's set to nBytes * 8. |
|
void StartReading( const void *pData, int nBytes, int iStartBit = 0, int nBits = -1 ); |
|
|
|
// Restart buffer reading. |
|
void Reset(); |
|
|
|
// Enable or disable assertion on overflow. 99% of the time, it's a bug that we need to catch, |
|
// but there may be the occasional buffer that is allowed to overflow gracefully. |
|
void SetAssertOnOverflow( bool bAssert ); |
|
|
|
// This can be set to assign a name that gets output if the buffer overflows. |
|
const char* GetDebugName() const { return m_pDebugName; } |
|
void SetDebugName( const char *pName ); |
|
|
|
void ExciseBits( int startbit, int bitstoremove ); |
|
|
|
|
|
// Bit functions. |
|
public: |
|
|
|
// Returns 0 or 1. |
|
int ReadOneBit(); |
|
|
|
|
|
protected: |
|
|
|
unsigned int CheckReadUBitLong(int numbits); // For debugging. |
|
int ReadOneBitNoCheck(); // Faster version, doesn't check bounds and is inlined. |
|
bool CheckForOverflow(int nBits); |
|
|
|
|
|
public: |
|
|
|
// Get the base pointer. |
|
const unsigned char* GetBasePointer() { return m_pData; } |
|
|
|
BITBUF_INLINE int TotalBytesAvailable( void ) const |
|
{ |
|
return m_nDataBytes; |
|
} |
|
|
|
// Read a list of bits in. |
|
void ReadBits(void *pOut, int nBits); |
|
// Read a list of bits in, but don't overrun the destination buffer. |
|
// Returns the number of bits read into the buffer. The remaining |
|
// bits are skipped over. |
|
int ReadBitsClamped_ptr(void *pOut, size_t outSizeBytes, size_t nBits); |
|
// Helper 'safe' template function that infers the size of the destination |
|
// array. This version of the function should be preferred. |
|
// Usage: char databuffer[100]; |
|
// ReadBitsClamped( dataBuffer, msg->m_nLength ); |
|
template <typename T, size_t N> |
|
int ReadBitsClamped( T (&pOut)[N], size_t nBits ) |
|
{ |
|
return ReadBitsClamped_ptr( pOut, N * sizeof(T), nBits ); |
|
} |
|
|
|
float ReadBitAngle( int numbits ); |
|
|
|
unsigned int ReadUBitLong( int numbits ) RESTRICT; |
|
unsigned int ReadUBitLongNoInline( int numbits ) RESTRICT; |
|
unsigned int PeekUBitLong( int numbits ); |
|
int ReadSBitLong( int numbits ); |
|
|
|
// reads an unsigned integer with variable bit length |
|
unsigned int ReadUBitVar(); |
|
unsigned int ReadUBitVarInternal( int encodingType ); |
|
|
|
// reads a varint encoded integer |
|
uint32 ReadVarInt32(); |
|
uint64 ReadVarInt64(); |
|
int32 ReadSignedVarInt32(); |
|
int64 ReadSignedVarInt64(); |
|
|
|
// You can read signed or unsigned data with this, just cast to |
|
// a signed int if necessary. |
|
unsigned int ReadBitLong(int numbits, bool bSigned); |
|
|
|
float ReadBitCoord(); |
|
float ReadBitCoordMP( bool bIntegral, bool bLowPrecision ); |
|
float ReadBitFloat(); |
|
float ReadBitNormal(); |
|
void ReadBitVec3Coord( Vector& fa ); |
|
void ReadBitVec3Normal( Vector& fa ); |
|
void ReadBitAngles( QAngle& fa ); |
|
|
|
// Faster for comparisons but do not fully decode float values |
|
unsigned int ReadBitCoordBits(); |
|
unsigned int ReadBitCoordMPBits( bool bIntegral, bool bLowPrecision ); |
|
|
|
// Byte functions (these still read data in bit-by-bit). |
|
public: |
|
|
|
BITBUF_INLINE int ReadChar() { return (char)ReadUBitLong(8); } |
|
BITBUF_INLINE int ReadByte() { return ReadUBitLong(8); } |
|
BITBUF_INLINE int ReadShort() { return (short)ReadUBitLong(16); } |
|
BITBUF_INLINE int ReadWord() { return ReadUBitLong(16); } |
|
BITBUF_INLINE int32 ReadLong() { return ReadUBitLong(32); } |
|
int64 ReadLongLong(); |
|
float ReadFloat(); |
|
bool ReadBytes(void *pOut, int nBytes); |
|
|
|
// Returns false if bufLen isn't large enough to hold the |
|
// string in the buffer. |
|
// |
|
// Always reads to the end of the string (so you can read the |
|
// next piece of data waiting). |
|
// |
|
// If bLine is true, it stops when it reaches a '\n' or a null-terminator. |
|
// |
|
// pStr is always null-terminated (unless bufLen is 0). |
|
// |
|
// pOutNumChars is set to the number of characters left in pStr when the routine is |
|
// complete (this will never exceed bufLen-1). |
|
// |
|
bool ReadString( char *pStr, int bufLen, bool bLine=false, int *pOutNumChars=NULL ); |
|
|
|
// Reads a string and allocates memory for it. If the string in the buffer |
|
// is > 2048 bytes, then pOverflow is set to true (if it's not NULL). |
|
char* ReadAndAllocateString( bool *pOverflow = 0 ); |
|
|
|
// Returns nonzero if any bits differ |
|
int CompareBits( bf_read * RESTRICT other, int bits ) RESTRICT; |
|
int CompareBitsAt( int offset, bf_read * RESTRICT other, int otherOffset, int bits ) RESTRICT; |
|
|
|
// Status. |
|
public: |
|
int GetNumBytesLeft(); |
|
int GetNumBytesRead(); |
|
int GetNumBitsLeft() RESTRICT; |
|
int GetNumBitsRead() const; |
|
|
|
// Has the buffer overflowed? |
|
inline bool IsOverflowed() const {return m_bOverflow;} |
|
|
|
inline bool Seek(int iBit); // Seek to a specific bit. |
|
inline bool SeekRelative(int iBitDelta); // Seek to an offset from the current position. |
|
|
|
// Called when the buffer is overflowed. |
|
void SetOverflowFlag() RESTRICT; |
|
|
|
|
|
public: |
|
|
|
// The current buffer. |
|
const unsigned char* RESTRICT m_pData; |
|
int m_nDataBytes; |
|
int m_nDataBits; |
|
|
|
// Where we are in the buffer. |
|
int m_iCurBit; |
|
|
|
|
|
private: |
|
// Errors? |
|
bool m_bOverflow; |
|
|
|
// For debugging.. |
|
bool m_bAssertOnOverflow; |
|
|
|
const char *m_pDebugName; |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Inlines. |
|
//----------------------------------------------------------------------------- |
|
|
|
inline int bf_read::GetNumBytesRead() |
|
{ |
|
return BitByte(m_iCurBit); |
|
} |
|
|
|
inline int bf_read::GetNumBitsLeft() RESTRICT |
|
{ |
|
return m_nDataBits - m_iCurBit; |
|
} |
|
|
|
inline int bf_read::GetNumBytesLeft() |
|
{ |
|
return GetNumBitsLeft() >> 3; |
|
} |
|
|
|
inline int bf_read::GetNumBitsRead() const |
|
{ |
|
return m_iCurBit; |
|
} |
|
|
|
inline bool bf_read::Seek(int iBit) |
|
{ |
|
if(iBit < 0 || iBit > m_nDataBits) |
|
{ |
|
SetOverflowFlag(); |
|
m_iCurBit = m_nDataBits; |
|
return false; |
|
} |
|
else |
|
{ |
|
m_iCurBit = iBit; |
|
return true; |
|
} |
|
} |
|
|
|
// Seek to an offset from the current position. |
|
inline bool bf_read::SeekRelative(int iBitDelta) |
|
{ |
|
return Seek(m_iCurBit+iBitDelta); |
|
} |
|
|
|
inline bool bf_read::CheckForOverflow(int nBits) |
|
{ |
|
if( m_iCurBit + nBits > m_nDataBits ) |
|
{ |
|
SetOverflowFlag(); |
|
CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); |
|
} |
|
|
|
return m_bOverflow; |
|
} |
|
|
|
inline int bf_read::ReadOneBitNoCheck() |
|
{ |
|
#if VALVE_LITTLE_ENDIAN |
|
unsigned int value = ((uint32 * RESTRICT)m_pData)[m_iCurBit >> 5] >> (m_iCurBit & 31); |
|
#else |
|
unsigned char value = m_pData[m_iCurBit >> 3] >> (m_iCurBit & 7); |
|
#endif |
|
++m_iCurBit; |
|
return value & 1; |
|
} |
|
|
|
inline int bf_read::ReadOneBit() |
|
{ |
|
if( GetNumBitsLeft() <= 0 ) |
|
{ |
|
SetOverflowFlag(); |
|
CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); |
|
return 0; |
|
} |
|
return ReadOneBitNoCheck(); |
|
} |
|
|
|
inline float bf_read::ReadBitFloat() |
|
{ |
|
union { uint32 u; float f; } c = { ReadUBitLong(32) }; |
|
return c.f; |
|
} |
|
|
|
BITBUF_INLINE unsigned int bf_read::ReadUBitVar() |
|
{ |
|
// six bits: low 2 bits for encoding + first 4 bits of value |
|
unsigned int sixbits = ReadUBitLong(6); |
|
unsigned int encoding = sixbits & 3; |
|
if ( encoding ) |
|
{ |
|
// this function will seek back four bits and read the full value |
|
return ReadUBitVarInternal( encoding ); |
|
} |
|
return sixbits >> 2; |
|
} |
|
|
|
BITBUF_INLINE unsigned int bf_read::ReadUBitLong( int numbits ) RESTRICT |
|
{ |
|
Assert( numbits > 0 && numbits <= 32 ); |
|
|
|
if ( GetNumBitsLeft() < numbits ) |
|
{ |
|
m_iCurBit = m_nDataBits; |
|
SetOverflowFlag(); |
|
CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); |
|
return 0; |
|
} |
|
|
|
unsigned int iStartBit = m_iCurBit & 31u; |
|
int iLastBit = m_iCurBit + numbits - 1; |
|
unsigned int iWordOffset1 = m_iCurBit >> 5; |
|
unsigned int iWordOffset2 = iLastBit >> 5; |
|
m_iCurBit += numbits; |
|
|
|
#if __i386__ |
|
unsigned int bitmask = (2 << (numbits-1)) - 1; |
|
#else |
|
extern uint32 g_ExtraMasks[33]; |
|
unsigned int bitmask = g_ExtraMasks[numbits]; |
|
#endif |
|
|
|
unsigned int dw1 = LoadLittleDWord( (uint32* RESTRICT)m_pData, iWordOffset1 ) >> iStartBit; |
|
unsigned int dw2 = LoadLittleDWord( (uint32* RESTRICT)m_pData, iWordOffset2 ) << (32 - iStartBit); |
|
|
|
return (dw1 | dw2) & bitmask; |
|
} |
|
|
|
BITBUF_INLINE int bf_read::CompareBits( bf_read * RESTRICT other, int numbits ) RESTRICT |
|
{ |
|
return (ReadUBitLong(numbits) != other->ReadUBitLong(numbits)); |
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|