diff --git a/r5dev/core/assert.h b/r5dev/core/assert.h index 6f5a9b12..e4c2e88f 100644 --- a/r5dev/core/assert.h +++ b/r5dev/core/assert.h @@ -10,6 +10,7 @@ // } while (false) //#else # define Assert(condition, ...) assert(condition) +# define AssertFatalMsg Assert // TODO: this needs to go to dbg.h # define AssertMsg(condition, ...) assert(condition) diff --git a/r5dev/core/shared_pch.h b/r5dev/core/shared_pch.h index bdfa5fe3..736212cd 100644 --- a/r5dev/core/shared_pch.h +++ b/r5dev/core/shared_pch.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/r5dev/core/stdafx.h b/r5dev/core/stdafx.h index a0f6da69..108f4f6d 100644 --- a/r5dev/core/stdafx.h +++ b/r5dev/core/stdafx.h @@ -60,10 +60,11 @@ #pragma warning(pop) // Tier0 includes. +#include "tier0/basetypes.h" +#include "tier0/wchartypes.h" #include "tier0/memaddr.h" #include "tier0/utility.h" #include "tier0/module.h" -#include "tier0/basetypes.h" #include "tier0/platform.h" #include "tier0/platwindow.h" #include "tier0/annotations.h" diff --git a/r5dev/public/coordsize.h b/r5dev/public/coordsize.h new file mode 100644 index 00000000..8666768f --- /dev/null +++ b/r5dev/public/coordsize.h @@ -0,0 +1,98 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef COORDSIZE_H +#define COORDSIZE_H +#pragma once + +#include "worldsize.h" + +// OVERALL Coordinate Size Limits used in COMMON.C MSG_*BitCoord() Routines (and someday the HUD) +#define COORD_INTEGER_BITS 16 +#define COORD_FRACTIONAL_BITS 5 +#define COORD_DENOMINATOR (1<<(COORD_FRACTIONAL_BITS)) +#define COORD_RESOLUTION (1.0f/(COORD_DENOMINATOR)) + +// Special threshold for networking multiplayer origins +#define COORD_INTEGER_BITS_MP 11 +#define COORD_FRACTIONAL_BITS_MP_LOWPRECISION 3 +#define COORD_DENOMINATOR_LOWPRECISION (1<<(COORD_FRACTIONAL_BITS_MP_LOWPRECISION)) +#define COORD_RESOLUTION_LOWPRECISION (1.0f/(COORD_DENOMINATOR_LOWPRECISION)) + +#define NORMAL_FRACTIONAL_BITS 11 +#define NORMAL_DENOMINATOR ((1<<(NORMAL_FRACTIONAL_BITS))-1) +#define NORMAL_RESOLUTION (1.0f/(NORMAL_DENOMINATOR)) + +// this is limited by the network fractional bits used for coords +// because net coords will be only be accurate to 5 bits fractional +// Standard collision test epsilon +// 1/32nd inch collision epsilon +#define DIST_EPSILON (0.03125) + +// Verify that coordsize.h and worldsize.h are consistently defined +#if (MAX_COORD_INTEGER != (1<> 1) ^ -static_cast(n & 1); } - const int kMaxVarintBytes = 10; const int kMaxVarint32Bytes = 5; + const int kMaxVarint64Bytes = 10; } +//----------------------------------------------------------------------------- +enum EBitCoordType +{ + kCW_None, + kCW_LowPrecision, + kCW_Integral +}; + +//----------------------------------------------------------------------------- class CBitBuffer { public: CBitBuffer(void); - FORCEINLINE void SetDebugName(const char* pName) { m_pDebugName = pName; } - FORCEINLINE const char* GetDebugName() const { return m_pDebugName; } - FORCEINLINE bool IsOverflowed() const { return m_bOverflow; } - FORCEINLINE void SetOverflowFlag() { m_bOverflow = true; } + + FORCEINLINE void SetDebugName(const char* pName) { m_pDebugName = pName; } + FORCEINLINE const char* GetDebugName() const { return m_pDebugName; } + FORCEINLINE bool IsOverflowed() const { return m_bOverflow; } + FORCEINLINE void SetOverflowFlag() { m_bOverflow = true; } //////////////////////////////////// const char* m_pDebugName; - uint8_t m_bOverflow; - int64_t m_nDataBits; + bool m_bOverflow; + ssize_t m_nDataBits; size_t m_nDataBytes; + + static const uint32 s_nMaskTable[33]; // 0 1 3 7 15 .. }; +//----------------------------------------------------------------------------- class CBitRead : public CBitBuffer { public: - CBitRead(const void* pData, int nBytes, int nBits = -1); - CBitRead(const char* pDebugName, const void* pData, int nBytes, int nBits = -1); + CBitRead(const void* pData, size_t nBytes, ssize_t nBits = -1); + CBitRead(const char* pDebugName, const void* pData, size_t nBytes, ssize_t nBits = -1); CBitRead(void) : CBitBuffer() { } - void StartReading(const void* pData, size_t nBytes, int64 iStartBit = 0, int64 nBits = -1); + void StartReading(const void* pData, size_t nBytes, ssize_t iStartBit = 0, ssize_t nBits = -1); - bool Seek(int64 nPosition); - FORCEINLINE int64 Tell(void) const; + FORCEINLINE ssize_t Tell(void) const; + bool Seek(ssize_t nPosition); - void GrabNextDWord(bool bOverFlowImmediately = false); - void FetchNext(); + FORCEINLINE bool SeekRelative(ssize_t nOffset) + { + return Seek(GetNumBitsRead() + nOffset); + } - FORCEINLINE int64 GetNumBitsRead(void) const { return Tell(); }; - FORCEINLINE int64 GetNumBytesRead(void) const { return ((GetNumBitsRead() + 7) >> 3); } + FORCEINLINE unsigned char const* GetBasePointer() + { + return reinterpret_cast(m_pData); + } - FORCEINLINE int64 GetNumBitsLeft() const { return m_nDataBits - GetNumBitsRead(); } - FORCEINLINE int64 GetNumBytesLeft() const { return GetNumBitsLeft() >> 3; } + FORCEINLINE ssize_t GetNumBitsRead(void) const { return Tell(); }; + FORCEINLINE ssize_t GetNumBytesRead(void) const { return ((GetNumBitsRead() + 7) >> 3); } - int ReadSBitLong(int numbits); - uint32 ReadUBitLong(int numbits); + FORCEINLINE ssize_t GetNumBitsLeft(void) const { return m_nDataBits - GetNumBitsRead(); } + FORCEINLINE ssize_t GetNumBytesLeft(void) const { return GetNumBitsLeft() >> 3; } - FORCEINLINE int ReadChar() { return ReadSBitLong(sizeof(char) << 3); } - FORCEINLINE int ReadByte() { return ReadSBitLong(sizeof(unsigned char) << 3); } - FORCEINLINE int ReadShort() { return ReadUBitLong(sizeof(short) << 3); } - FORCEINLINE int ReadWord() { return ReadUBitLong(sizeof(unsigned short) << 3); } - FORCEINLINE int ReadLong() { return ReadUBitLong(sizeof(int32) << 3); } + FORCEINLINE size_t TotalBytesAvailable(void) const { return m_nDataBytes; } - int64 ReadLongLong(); - float ReadFloat(); - void ReadBits(void* pOutData, int nBits); - bool ReadBytes(void* pOut, int nBytes); - bool ReadString(char* pStr, int bufLen, bool bLine = false, int* pOutNumChars = nullptr); + FORCEINLINE void GrabNextDWord(bool bOverFlowImmediately = false); + FORCEINLINE void FetchNext(void); + + FORCEINLINE unsigned int ReadUBitLong(int numbits); + FORCEINLINE int ReadSBitLong(int numbits); + FORCEINLINE unsigned int PeekUBitLong(int numbits); + + FORCEINLINE unsigned int ReadUBitVar(void); + + FORCEINLINE float ReadBitFloat(void) + { + uint32 nvalue = ReadUBitLong(32); + return *((float*)&nvalue); + } + + + float ReadBitCoord(); + float ReadBitCoordMP(EBitCoordType coordType); + float ReadBitCellCoord(int bits, EBitCoordType coordType); + float ReadBitNormal(); + void ReadBitVec3Coord(Vector3D& fa); + void ReadBitVec3Normal(Vector3D& fa); + void ReadBitAngles(QAngle& fa); + float ReadBitAngle(int numbits); + + // returns 0 or 1. + FORCEINLINE int ReadOneBit(void); + + FORCEINLINE int ReadChar(void) { return ReadSBitLong(sizeof(char) << 3); } + FORCEINLINE int ReadByte(void) { return ReadSBitLong(sizeof(unsigned char) << 3); } + FORCEINLINE int ReadShort(void) { return ReadUBitLong(sizeof(short) << 3); } + FORCEINLINE int ReadWord(void) { return ReadUBitLong(sizeof(unsigned short) << 3); } + FORCEINLINE int ReadLong(void) { return ReadUBitLong(sizeof(int32) << 3); } + FORCEINLINE float ReadFloat(void) + { + uint32 nUval = ReadUBitLong(sizeof(int32) << 3); + return *((float*)&nUval); + } + + // reads a signed 64-bit integer from the buffer + int64 ReadLongLong(void); + + // reads a varint encoded integer + uint32 ReadVarInt32(); + uint64 ReadVarInt64(); + int32 ReadSignedVarInt32() { return bitbuf::ZigZagDecode32(ReadVarInt32()); } + int64 ReadSignedVarInt64() { return bitbuf::ZigZagDecode64(ReadVarInt64()); } + + void ReadBits(void* pOutData, int nBits); + 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); + bool ReadWString(wchar_t* 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 = NULL); //////////////////////////////////// uint32 m_nInBufWord; @@ -163,6 +243,7 @@ public: const uint32* m_pData; }; +//----------------------------------------------------------------------------- class CBitWrite { public: @@ -174,44 +255,88 @@ public: CBitWrite(const char* pDebugName, void* pData, int nBytes, int nMaxBits = -1); // Restart buffer writing. - inline void Reset() { m_iCurBit = 0; m_bOverflow = false; } - inline void SeekToBit(int bitPos) { m_iCurBit = bitPos; } + inline void Reset() { m_iCurBit = 0; m_bOverflow = false; } + inline void SeekToBit(int bitPos) { m_iCurBit = bitPos; } void StartWriting(void* pData, int nBytes, int iStartBit = 0, int nMaxBits = -1); // Bit functions. - void WriteOneBit(int nValue); - void WriteOneBitNoCheck(int nValue); - void WriteOneBitAt(int iBit, int nValue); + 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 curData, int numbits, bool bCheckRange = false); - void WriteSBitLong(int data, int numbits); + void WriteUBitLong(unsigned int curData, int numbits, bool bCheckRange = false); + 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); + void WriteBitLong(unsigned int data, int numbits, bool bSigned); // Write a list of bits in. - bool WriteBits(const void* pIn, int nBits); - inline bool WriteBytes(const void* pIn, int nBytes) { return WriteBits(pIn, nBytes << 3); } + bool WriteBits(const void* pIn, int nBits); + + // copy the bits straight out of pIn. This seeks pIn forward by nBits, + // returns false if this buffer or the read buffer overflows + bool WriteBitsFromBuffer(class bf_read *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); + + // writes an angle or vector encoded + void WriteBitAngle(float fAngle, int numbits); + void WriteBitCoord(const float f); + void WriteBitCoordMP(const float f, bool bIntegral, bool bLowPrecision); + void WriteBitCellCoord(const float f, int bits, EBitCoordType coordType); + void WriteBitFloat(float val); + void WriteBitVec3Coord(const Vector3D& fa); + void WriteBitNormal(float f); + void WriteBitVec3Normal(const Vector3D& fa); + void WriteBitAngles(const QAngle& fa); + + // byte functions + void WriteChar(int val); + void WriteByte(int val); + void WriteShort(int val); + void WriteWord(int val); + void WriteLong(long 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); + bool WriteWString(const wchar_t* pStr); // How many bytes are filled in? - FORCEINLINE int GetNumBytesWritten() const { return BitByte(this->m_iCurBit); } - FORCEINLINE int GetNumBitsWritten() const { return this->m_iCurBit; } - FORCEINLINE int GetMaxNumBits() const { return this->m_nDataBits; } - FORCEINLINE int GetNumBitsLeft() const { return this->m_nDataBits - m_iCurBit; } - FORCEINLINE int GetNumBytesLeft() const { return this->GetNumBitsLeft() >> 3; } - FORCEINLINE unsigned char* GetData() const { return this->m_pData; } + FORCEINLINE int GetNumBytesWritten() const { return BitByte(this->m_iCurBit); } + FORCEINLINE int GetNumBitsWritten() const { return this->m_iCurBit; } + FORCEINLINE int GetMaxNumBits() const { return this->m_nDataBits; } + FORCEINLINE int GetNumBitsLeft() const { return this->m_nDataBits - m_iCurBit; } + FORCEINLINE int GetNumBytesLeft() const { return this->GetNumBitsLeft() >> 3; } - inline const char* GetDebugName() const { return this->m_pDebugName; } - inline void SetDebugName(const char* pDebugName) { m_pDebugName = pDebugName; } + FORCEINLINE unsigned char* GetData() { return this->m_pData; } + FORCEINLINE const unsigned char* GetData() const { return this->m_pData; } + + FORCEINLINE const char* GetDebugName() const { return this->m_pDebugName; } + FORCEINLINE void SetDebugName(const char* pDebugName) { m_pDebugName = pDebugName; } // Has the buffer overflowed? bool CheckForOverflow(int nBits); void SetOverflowFlag(); FORCEINLINE bool IsOverflowed() const { return this->m_bOverflow; } + private: // The current buffer. unsigned char* m_pData; @@ -233,7 +358,14 @@ private: class bf_read : public CBitRead { public: + bf_read(const void* pData, int nBytes, int nBits = -1) + : CBitRead(pData, nBytes, nBits) {} + bf_read(const char* pDebugName, void* pData, int nBytes, int nMaxBits = -1) + : CBitRead(pDebugName, pData, nMaxBits) {} + + bf_read(void) : CBitRead() + {} }; //----------------------------------------------------------------------------- @@ -242,22 +374,264 @@ public: class bf_write : public CBitWrite { public: + bf_write(void* pData, int nBytes, int nMaxBits = -1) + : CBitWrite(pData, nBytes, nMaxBits) {} + bf_write(const char* pDebugName, void* pData, int nBytes, int nMaxBits = -1) + : CBitWrite(pDebugName, pData, nBytes, nMaxBits) {} + + bf_write(void) : CBitWrite() + {} }; + +/////////////////////////////////////////////////////////////////////////////// +// Routines for getting positions and grabbing next data in the read buffer +/////////////////////////////////////////////////////////////////////////////// + //----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -FORCEINLINE int64 CBitRead::Tell(void) const +FORCEINLINE ssize_t CBitRead::Tell(void) const { if (!m_pData) // pesky null ptr bitbufs. these happen. + { + Assert(m_pData); return 0; + } - int64 nCurOfs = int64(((intp(m_pDataIn) - intp(m_pData)) / 4) - 1); + ssize_t nCurOfs = int64(((intp(m_pDataIn) - intp(m_pData)) / 4) - 1); nCurOfs *= 32; nCurOfs += (32 - m_nBitsAvail); - int64 nAdjust = 8 * (m_nDataBytes & 3); + ssize_t nAdjust = 8 * (m_nDataBytes & 3); + return MIN(nCurOfs + nAdjust, m_nDataBits); } +//----------------------------------------------------------------------------- +FORCEINLINE void CBitRead::GrabNextDWord(bool bOverFlowImmediately) +{ + if (m_pDataIn == m_pBufferEnd) + { + m_nBitsAvail = 1; + m_nInBufWord = 0; + + m_pDataIn++; + + if (bOverFlowImmediately) + SetOverflowFlag(); + } + else + { + if (m_pDataIn > m_pBufferEnd) + { + SetOverflowFlag(); + m_nInBufWord = 0; + } + else + { + assert(reinterpret_cast(m_pDataIn) + 3 < reinterpret_cast(m_pBufferEnd)); + m_nInBufWord = LittleDWord(*(m_pDataIn++)); + } + } +} + +//----------------------------------------------------------------------------- +FORCEINLINE void CBitRead::FetchNext() +{ + m_nBitsAvail = 32; + GrabNextDWord(false); +} + +//----------------------------------------------------------------------------- +FORCEINLINE int CBitRead::ReadOneBit(void) +{ + int nRet = m_nInBufWord & 1; + if (--m_nBitsAvail == 0) + { + FetchNext(); + } + else + m_nInBufWord >>= 1; + return nRet; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for reading encoded integers from the buffer +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +// reads an unsigned integer from the buffer +FORCEINLINE unsigned int CBitRead::ReadUBitLong(int numbits) +{ + if (m_nBitsAvail >= numbits) + { + unsigned int nRet = m_nInBufWord & s_nMaskTable[numbits]; + m_nBitsAvail -= numbits; + if (m_nBitsAvail) + { + m_nInBufWord >>= numbits; + } + else + { + FetchNext(); + } + return nRet; + } + else + { + uint32 nRet = m_nInBufWord; + numbits -= m_nBitsAvail; + GrabNextDWord(true); + + if (IsOverflowed()) + return 0; + + nRet |= ((m_nInBufWord & s_nMaskTable[numbits]) << m_nBitsAvail); + m_nBitsAvail = 32 - numbits; + m_nInBufWord >>= numbits; + + return nRet; + } +} + +//----------------------------------------------------------------------------- +// reads a signed integer from the buffer +FORCEINLINE int CBitRead::ReadSBitLong(int numbits) +{ + int nRet = ReadUBitLong(numbits); + return (nRet << (32 - numbits)) >> (32 - numbits); +} + +//----------------------------------------------------------------------------- +// peeks an unsigned integer from the buffer +FORCEINLINE unsigned int CBitRead::PeekUBitLong(int numbits) +{ + int nSaveBA = m_nBitsAvail; + uint32_t nSaveW = m_nInBufWord; + uint32 const* pSaveP = m_pDataIn; + unsigned int nRet = ReadUBitLong(numbits); + m_nBitsAvail = nSaveBA; + m_nInBufWord = nSaveW; + m_pDataIn = pSaveP; + return nRet; +} + +#ifdef _WIN32 +#pragma warning(push) +#pragma warning(disable : 4715) // disable warning on not all cases + // returning a value. throwing default: + // in measurably reduces perf in bit + // packing benchmark +#endif +//----------------------------------------------------------------------------- +// reads an unsigned integer with variable bit length +FORCEINLINE unsigned int CBitRead::ReadUBitVar(void) +{ + unsigned int ret = ReadUBitLong(6); + switch (ret & (16 | 32)) + { + case 16: + ret = (ret & 15) | (ReadUBitLong(4) << 4); + Assert(ret >= 16); + break; + + case 32: + ret = (ret & 15) | (ReadUBitLong(8) << 4); + Assert(ret >= 256); + break; + case 48: + ret = (ret & 15) | (ReadUBitLong(32 - 4) << 4); + Assert(ret >= 4096); + break; + } + return ret; +} +#ifdef _WIN32 +#pragma warning(pop) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for bit level writing operations +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +// writes a bit to the buffer without checking for overflow +FORCEINLINE void CBitWrite::WriteOneBitNoCheck(int nValue) +{ + if (nValue) + m_pData[m_iCurBit >> 3] |= (1 << (m_iCurBit & 7)); + else + m_pData[m_iCurBit >> 3] &= ~(1 << (m_iCurBit & 7)); + + ++m_iCurBit; +} + +//----------------------------------------------------------------------------- +// writes a bit to the buffer +FORCEINLINE void CBitWrite::WriteOneBit(int nValue) +{ + if (!CheckForOverflow(1)) + WriteOneBitNoCheck(nValue); +} + +//----------------------------------------------------------------------------- +// writes a bit to the buffer at a specific bit index +FORCEINLINE void CBitWrite::WriteOneBitAt(int iBit, int nValue) +{ + if (iBit + 1 > m_nDataBits) + { + SetOverflowFlag(); + CallErrorHandler(BITBUFERROR_BUFFER_OVERRUN, GetDebugName()); + return; + } + + if (nValue) + m_pData[iBit >> 3] |= (1 << (iBit & 7)); + else + m_pData[iBit >> 3] &= ~(1 << (iBit & 7)); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for writing integers into the buffer +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +// writes an unsigned integer with variable bit length +FORCEINLINE void CBitWrite::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 +FORCEINLINE void CBitWrite::WriteBitFloat(float val) +{ + long intVal; + + Assert(sizeof(long) == sizeof(float)); + Assert(sizeof(float) == 4); + + intVal = *((long*)&val); + WriteUBitLong(intVal, 32); +} + #endif // BITBUF_H diff --git a/r5dev/tier1/bitbuf.cpp b/r5dev/tier1/bitbuf.cpp index f71f883d..780c84e2 100644 --- a/r5dev/tier1/bitbuf.cpp +++ b/r5dev/tier1/bitbuf.cpp @@ -6,45 +6,76 @@ //===========================================================================// #include "tier1/bitbuf.h" -#include "mathlib/swap.h" #include "mathlib/bitvec.h" +#include "public/coordsize.h" +// Precalculated bit masks for WriteUBitLong. Using these tables instead of +// doing the calculations gives a 33% speedup in WriteUBitLong. +uint32 g_BitWriteMasks[32][33]; + +// (1 << i) - 1 +uint32 g_ExtraMasks[32]; //----------------------------------------------------------------------------- -// Write masks +// Read/Write masks //----------------------------------------------------------------------------- -class CBitWriteMasks +class CBitWriteMasksInit { public: - CBitWriteMasks() + CBitWriteMasksInit() { for (unsigned int startbit = 0; startbit < 32; startbit++) { for (unsigned int nBitsLeft = 0; nBitsLeft < 33; nBitsLeft++) { unsigned int endbit = startbit + nBitsLeft; - m_BitWriteMasks[startbit][nBitsLeft] = GetBitForBitnum(int(startbit)) - 1; + g_BitWriteMasks[startbit][nBitsLeft] = GetBitForBitnum(startbit) - 1; if (endbit < 32) - m_BitWriteMasks[startbit][nBitsLeft] |= ~(GetBitForBitnum(int(endbit)) - 1); + g_BitWriteMasks[startbit][nBitsLeft] |= ~(GetBitForBitnum(endbit) - 1); } } for (unsigned int maskBit = 0; maskBit < 32; maskBit++) - m_ExtraMasks[maskBit] = GetBitForBitnum(int(maskBit)) - 1; - m_ExtraMasks[32] = ~0ul; - - for (unsigned int littleBit = 0; littleBit < 32; littleBit++) - StoreLittleDWord((unsigned int*)&m_LittleBits[littleBit], 0, 1u << littleBit); + g_ExtraMasks[maskBit] = GetBitForBitnum(maskBit) - 1; } - - // Precalculated bit masks for WriteUBitLong. Using these tables instead of - // doing the calculations gives a 33% speedup in WriteUBitLong. - unsigned long m_BitWriteMasks[32][33]; - unsigned long m_LittleBits[32]; - unsigned long m_ExtraMasks[33]; // (1 << i) - 1 }; -static CBitWriteMasks s_BitWriteMasks; +static CBitWriteMasksInit s_BitWriteMasksInit; +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, +}; //----------------------------------------------------------------------------- // Error handler @@ -63,7 +94,6 @@ void SetBitBufErrorHandler(BitBufErrorHandler fn) // ---------------------------------------------------------------------------------------- // // CBitBuffer // ---------------------------------------------------------------------------------------- // - CBitBuffer::CBitBuffer(void) { m_bOverflow = false; @@ -75,24 +105,27 @@ CBitBuffer::CBitBuffer(void) // ---------------------------------------------------------------------------------------- // // bf_read // ---------------------------------------------------------------------------------------- // -CBitRead::CBitRead(const void* pData, int nBytes, int nBits /*= -1*/) +CBitRead::CBitRead(const void* pData, size_t nBytes, ssize_t nBits /*= -1*/) { StartReading(pData, nBytes, 0, nBits); } - -CBitRead::CBitRead(const char* pDebugName, const void* pData, int nBytes, int nBits /*= -1*/) +CBitRead::CBitRead(const char* pDebugName, const void* pData, size_t nBytes, ssize_t nBits /*= -1*/) { SetDebugName(pDebugName); StartReading(pData, nBytes, 0, nBits); } + +/////////////////////////////////////////////////////////////////////////////// +// Core bf_read routines +/////////////////////////////////////////////////////////////////////////////// + //----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBitRead::StartReading(const void* pData, size_t nBytes, int64 iStartBit, int64 nBits) +void CBitRead::StartReading(const void* pData, size_t nBytes, ssize_t iStartBit, ssize_t nBits) { // Make sure it's dword aligned and padded. - assert((int64(pData) & 3) == 0); + assert((ssize_t(pData) & 3) == 0); + m_pData = (uint32*)pData; m_pDataIn = m_pData; m_nDataBytes = nBytes; @@ -103,20 +136,21 @@ void CBitRead::StartReading(const void* pData, size_t nBytes, int64 iStartBit, i } else { - assert(nBits <= int64(nBytes * 8)); + assert(nBits <= (ssize_t)(nBytes * 8)); m_nDataBits = nBits; } + m_bOverflow = false; m_pBufferEnd = reinterpret_cast (reinterpret_cast (m_pData) + nBytes); + if (m_pData) Seek(iStartBit); } //----------------------------------------------------------------------------- -// Purpose: seeks to a specific position in the buffer -//----------------------------------------------------------------------------- -bool CBitRead::Seek(int64 nPosition) +// seeks to a specific position in the buffer +bool CBitRead::Seek(ssize_t nPosition) { bool bSucc = true; if (nPosition < 0 || nPosition > m_nDataBits) @@ -161,97 +195,249 @@ bool CBitRead::Seek(int64 nPosition) m_nBitsAvail = 1; } m_nInBufWord >>= (nAdjPosition & 31); - m_nBitsAvail = min(m_nBitsAvail, 32 - (nAdjPosition & 31)); // in case grabnextdword overflowed + m_nBitsAvail = MIN(m_nBitsAvail, 32 - (nAdjPosition & 31)); // in case grabnextdword overflowed } return bSucc; } + +/////////////////////////////////////////////////////////////////////////////// +// Routines for reading coordinates from the buffer (these contain bit-field +// size AND fixed point scaling constants) +/////////////////////////////////////////////////////////////////////////////// + //----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBitRead::GrabNextDWord(bool bOverFlowImmediately) +float CBitRead::ReadBitCoord(void) { - if (m_pDataIn == m_pBufferEnd) + 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) { - m_nBitsAvail = 1; - m_nInBufWord = 0; + // Read the sign bit + signbit = ReadOneBit(); - m_pDataIn++; + // 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 (bOverFlowImmediately) - SetOverflowFlag(); + // If there's a fraction, read it in + if (fractval) + { + fractval = ReadUBitLong(COORD_FRACTIONAL_BITS); + } + + // Calculate the correct floating point value + value = intval + (float)((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 = (float)ReadUBitLong(COORD_INTEGER_BITS_MP) + 1; + } + else + { + value = (float)ReadUBitLong(COORD_INTEGER_BITS) + 1; + } + } } else { - if (m_pDataIn > m_pBufferEnd) + // 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) { - SetOverflowFlag(); - m_nInBufWord = 0; - } - else - { - assert(reinterpret_cast(m_pDataIn) + 3 < reinterpret_cast(m_pBufferEnd)); - m_nInBufWord = LittleDWord(*(m_pDataIn++)); + 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; } //----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBitRead::FetchNext() +float CBitRead::ReadBitCellCoord(int bits, EBitCoordType coordType) { - m_nBitsAvail = 32; - GrabNextDWord(false); -} +#if defined( BB_PROFILING ) + VPROF("CBitRead::ReadBitCoordMP"); +#endif + bool bIntegral = (coordType == kCW_Integral); + bool bLowPrecision = (coordType == kCW_LowPrecision); -//----------------------------------------------------------------------------- -// Purpose: reads an unsigned integer from the buffer -//----------------------------------------------------------------------------- -uint32 CBitRead::ReadUBitLong(int numbits) -{ - if (m_nBitsAvail >= numbits) + int intval = 0, fractval = 0; + float value = 0.0; + + if (bIntegral) { - unsigned int nRet = m_nInBufWord & s_BitWriteMasks.m_ExtraMasks[numbits]; - m_nBitsAvail -= numbits; - if (m_nBitsAvail) - { - m_nInBufWord >>= numbits; - } - else - { - FetchNext(); - } - return nRet; + value = (float)ReadUBitLong(bits); } else { - uint32 nRet = m_nInBufWord; - numbits -= m_nBitsAvail; - GrabNextDWord(true); + intval = ReadUBitLong(bits); - if (IsOverflowed()) - return 0; + // If there's a fraction, read it in + fractval = ReadUBitLong(bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS); - nRet |= ((m_nInBufWord & s_BitWriteMasks.m_ExtraMasks[numbits]) << m_nBitsAvail); - m_nBitsAvail = 32 - numbits; - m_nInBufWord >>= numbits; - - return nRet; + // Calculate the correct floating point value + value = intval + ((float)fractval * (bLowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION)); } + + return value; } //----------------------------------------------------------------------------- -// Purpose: reads a signed integer from the buffer -//----------------------------------------------------------------------------- -int CBitRead::ReadSBitLong(int numbits) +float CBitRead::ReadBitNormal(void) { - int nRet = ReadUBitLong(numbits); - return (nRet << (32 - numbits)) >> (32 - numbits); + // 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; } //----------------------------------------------------------------------------- -// Purpose: reads a signed 64-bit integer from the buffer +void CBitRead::ReadBitVec3Coord(Vector3D& 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(); +} + +//----------------------------------------------------------------------------- +void CBitRead::ReadBitVec3Normal(Vector3D& 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) +{ + Vector3D tmp; + ReadBitVec3Coord(tmp); + fa.Init(tmp.x, tmp.y, tmp.z); +} + +//----------------------------------------------------------------------------- +float CBitRead::ReadBitAngle(int numbits) +{ + float shift = (float)(GetBitForBitnum(numbits)); + + int i = ReadUBitLong(numbits); + float fReturn = (float)i * (360.0f / shift); + + return fReturn; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for reading encoded (var)ints from the buffer +/////////////////////////////////////////////////////////////////////////////// + //----------------------------------------------------------------------------- int64 CBitRead::ReadLongLong() { @@ -268,21 +454,63 @@ int64 CBitRead::ReadLongLong() } //----------------------------------------------------------------------------- -// Purpose: reads a float from the buffer -//----------------------------------------------------------------------------- -float CBitRead::ReadFloat() +// 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() { - float ret; - Assert(sizeof(ret) == 4); - ReadBits(&ret, 32); + uint32 result = 0; + int count = 0; + uint32 b; - // Swap the float, since ReadBits reads raw data - LittleFloat(&ret, &ret); - return ret; + do + { + if (count == bitbuf::kMaxVarint32Bytes) + { + return result; + } + b = ReadUBitLong(8); + result |= (b & 0x7F) << (7 * count); + ++count; + } while (b & 0x80); + + return result; } //----------------------------------------------------------------------------- -// Purpose: reads bits from the buffer +uint64 CBitRead::ReadVarInt64() +{ + uint64 result = 0; + int count = 0; + uint64 b; + + do + { + if (count == bitbuf::kMaxVarint64Bytes) + { + return result; + } + b = ReadUBitLong(8); + result |= static_cast(b & 0x7F) << (7 * count); + ++count; + } while (b & 0x80); + + return result; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for reading bits and bytes from the buffer +/////////////////////////////////////////////////////////////////////////////// + //----------------------------------------------------------------------------- void CBitRead::ReadBits(void* pOutData, int nBits) { @@ -324,8 +552,6 @@ void CBitRead::ReadBits(void* pOutData, int nBits) } } -//----------------------------------------------------------------------------- -// Purpose: reads bytes from the buffer //----------------------------------------------------------------------------- bool CBitRead::ReadBytes(void* pOut, int nBytes) { @@ -333,8 +559,11 @@ bool CBitRead::ReadBytes(void* pOut, int nBytes) return !IsOverflowed(); } -//----------------------------------------------------------------------------- -// Purpose: reads a string from the buffer + +/////////////////////////////////////////////////////////////////////////////// +// Routines for reading encoded strings from the buffer +/////////////////////////////////////////////////////////////////////////////// + //----------------------------------------------------------------------------- bool CBitRead::ReadString(char* pStr, int maxLen, bool bLine, int* pOutNumChars) { @@ -370,6 +599,60 @@ bool CBitRead::ReadString(char* pStr, int maxLen, bool bLine, int* pOutNumChars) 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 = (wchar)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; +} + // ---------------------------------------------------------------------------------------- // // bf_write // ---------------------------------------------------------------------------------------- // @@ -398,9 +681,11 @@ CBitWrite::CBitWrite(void* pData, int nBytes, int nBits) StartWriting(pData, nBytes, 0, nBits); } -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- + +/////////////////////////////////////////////////////////////////////////////// +// Core bf_write routines +/////////////////////////////////////////////////////////////////////////////// + void CBitWrite::StartWriting(void* pData, int nBytes, int iStartBit, int nBits) { // Make sure it's dword aligned and padded. @@ -429,49 +714,37 @@ void CBitWrite::StartWriting(void* pData, int nBytes, int iStartBit, int nBits) } //----------------------------------------------------------------------------- -// Purpose: writes a bit to the buffer without checking for overflow -//----------------------------------------------------------------------------- -inline void CBitWrite::WriteOneBitNoCheck(int nValue) +// checks if we have enough space for the requested number of bits +bool CBitWrite::CheckForOverflow(int nBits) { - if (nValue) - m_pData[m_iCurBit >> 3] |= (1 << (m_iCurBit & 7)); - else - m_pData[m_iCurBit >> 3] &= ~(1 << (m_iCurBit & 7)); - - ++m_iCurBit; -} - -//----------------------------------------------------------------------------- -// Purpose: writes a bit to the buffer -//----------------------------------------------------------------------------- -inline void CBitWrite::WriteOneBit(int nValue) -{ - if (!CheckForOverflow(1)) - WriteOneBitNoCheck(nValue); -} - -//----------------------------------------------------------------------------- -// Purpose: writes a bit to the buffer at a specific bit index -//----------------------------------------------------------------------------- -inline void CBitWrite::WriteOneBitAt(int iBit, int nValue) -{ - if (iBit + 1 > m_nDataBits) + if (this->m_iCurBit + nBits > this->m_nDataBits) { - SetOverflowFlag(); - CallErrorHandler(BITBUFERROR_BUFFER_OVERRUN, GetDebugName()); - return; + this->SetOverflowFlag(); } - if (nValue) - m_pData[iBit >> 3] |= (1 << (iBit & 7)); - else - m_pData[iBit >> 3] &= ~(1 << (iBit & 7)); + return IsOverflowed(); } //----------------------------------------------------------------------------- -// Purpose: writes an unsigned integer to the buffer +// sets the overflow flag +void CBitWrite::SetOverflowFlag() +{ + if (this->m_bAssertOnOverflow) + { + assert(false); + } + + this->m_bOverflow = true; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for writing integers into the buffer +/////////////////////////////////////////////////////////////////////////////// + //----------------------------------------------------------------------------- -/*BITBUF_INLINE*/ void CBitWrite::WriteUBitLong(unsigned int curData, int numbits, bool bCheckRange) +// writes an unsigned integer into the buffer +void CBitWrite::WriteUBitLong(unsigned int curData, int numbits, bool bCheckRange) { #ifdef _DEBUG // Make sure it doesn't overflow. @@ -507,7 +780,7 @@ inline void CBitWrite::WriteOneBitAt(int iBit, int nValue) uint32 dword = LoadLittleDWord((uint32*)m_pData, iDWord); - dword &= s_BitWriteMasks.m_BitWriteMasks[iCurBitMasked][nBitsLeft]; + dword &= g_BitWriteMasks[iCurBitMasked][nBitsLeft]; dword |= curData << iCurBitMasked; // write to stream (lsb to msb) properly @@ -523,7 +796,7 @@ inline void CBitWrite::WriteOneBitAt(int iBit, int nValue) // read from stream (lsb to msb) properly dword = LoadLittleDWord((uint32*)m_pData, iDWord + 1); - dword &= s_BitWriteMasks.m_BitWriteMasks[0][nBitsLeft]; + dword &= g_BitWriteMasks[0][nBitsLeft]; dword |= curData; // write to stream (lsb to msb) properly @@ -534,9 +807,7 @@ inline void CBitWrite::WriteOneBitAt(int iBit, int nValue) } //----------------------------------------------------------------------------- -// Purpose: writes a signed integer to the buffer -// (Sign bit comes first) -//----------------------------------------------------------------------------- +// NOTE: Sign bit comes first void CBitWrite::WriteSBitLong(int data, int numbits) { // Do we have a valid # of bits to encode with? @@ -572,8 +843,6 @@ void CBitWrite::WriteSBitLong(int data, int numbits) } } //----------------------------------------------------------------------------- -// Purpose: writes a signed or unsigned integer to the buffer -//----------------------------------------------------------------------------- void CBitWrite::WriteBitLong(unsigned int data, int numbits, bool bSigned) { if (bSigned) @@ -582,8 +851,6 @@ void CBitWrite::WriteBitLong(unsigned int data, int numbits, bool bSigned) WriteUBitLong(data, numbits); } -//----------------------------------------------------------------------------- -// Purpose: writes a list of bits to the buffer //----------------------------------------------------------------------------- bool CBitWrite::WriteBits(const void* pInData, int nBits) { @@ -628,7 +895,7 @@ bool CBitWrite::WriteBits(const void* pInData, int nBits) unsigned int iDWord = m_iCurBit >> 5; uint32 outWord = LoadLittleDWord((uint32*)m_pData, iDWord); - outWord &= s_BitWriteMasks.m_BitWriteMasks[iBitsRight][32]; // clear rest of beginning DWORD + outWord &= g_BitWriteMasks[iBitsRight][32]; // clear rest of beginning DWORD // copy in DWORD blocks while (nBitsLeft >= iBitsChanging) @@ -677,27 +944,559 @@ bool CBitWrite::WriteBits(const void* pInData, int nBits) } //----------------------------------------------------------------------------- -// Purpose: checks if we have enough space for the requested number of bits -//----------------------------------------------------------------------------- -bool CBitWrite::CheckForOverflow(int nBits) +bool CBitWrite::WriteBitsFromBuffer(bf_read* pIn, int nBits) { - if (this->m_iCurBit + nBits > this->m_nDataBits) + // This could be optimized a little by + while (nBits > 32) { - this->SetOverflowFlag(); + WriteUBitLong(pIn->ReadUBitLong(32), 32); + nBits -= 32; } - return IsOverflowed(); + WriteUBitLong(pIn->ReadUBitLong(nBits), nBits); + return !IsOverflowed() && !pIn->IsOverflowed(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for writing integers with variable bit length into the buffer +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +void CBitWrite::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(data | 0x80); + if (data >= (1 << 7)) + { + target[1] = static_cast((data >> 7) | 0x80); + if (data >= (1 << 14)) + { + target[2] = static_cast((data >> 14) | 0x80); + if (data >= (1 << 21)) + { + target[3] = static_cast((data >> 21) | 0x80); + if (data >= (1 << 28)) + { + target[4] = static_cast(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); + } } //----------------------------------------------------------------------------- -// Purpose: sets the overflow flag -//----------------------------------------------------------------------------- -void CBitWrite::SetOverflowFlag() +void CBitWrite::WriteVarInt64(uint64 data) { - if (this->m_bAssertOnOverflow) + // Check if align and we have room, slow path if not + if ((m_iCurBit & 7) == 0 && (m_iCurBit + bitbuf::kMaxVarint64Bytes * 8) <= m_nDataBits) { - assert(false); + uint8* target = ((uint8*)m_pData) + (m_iCurBit >> 3); + + // Splitting into 32-bit pieces gives better performance on 32-bit + // processors. + uint32 part0 = static_cast(data); + uint32 part1 = static_cast(data >> 28); + uint32 part2 = static_cast(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; + } + } + + // commented as this would otherwise trigger a compiled warning in MSVC + // which confirms this code is unreachable + //AssertFatalMsg(false, "Can't get here."); + + size10: target[9] = static_cast((part2 >> 7) | 0x80); + size9: target[8] = static_cast((part2) | 0x80); + size8: target[7] = static_cast((part1 >> 21) | 0x80); + size7: target[6] = static_cast((part1 >> 14) | 0x80); + size6: target[5] = static_cast((part1 >> 7) | 0x80); + size5: target[4] = static_cast((part1) | 0x80); + size4: target[3] = static_cast((part0 >> 21) | 0x80); + size3: target[2] = static_cast((part0 >> 14) | 0x80); + size2: target[1] = static_cast((part0 >> 7) | 0x80); + size1: target[0] = static_cast((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 CBitWrite::WriteSignedVarInt32(int32 data) +{ + WriteVarInt32(bitbuf::ZigZagEncode32(data)); +} + +//----------------------------------------------------------------------------- +void CBitWrite::WriteSignedVarInt64(int64 data) +{ + WriteVarInt64(bitbuf::ZigZagEncode64(data)); +} + +//----------------------------------------------------------------------------- +int CBitWrite::ByteSizeVarInt32(uint32 data) +{ + int size = 1; + while (data > 0x7F) { + size++; + data >>= 7; + } + return size; +} + +//----------------------------------------------------------------------------- +int CBitWrite::ByteSizeVarInt64(uint64 data) +{ + int size = 1; + while (data > 0x7F) { + size++; + data >>= 7; + } + return size; +} + +//----------------------------------------------------------------------------- +int CBitWrite::ByteSizeSignedVarInt32(int32 data) +{ + return ByteSizeVarInt32(bitbuf::ZigZagEncode32(data)); +} + +//----------------------------------------------------------------------------- +int CBitWrite::ByteSizeSignedVarInt64(int64 data) +{ + return ByteSizeVarInt64(bitbuf::ZigZagEncode64(data)); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for writing coordinates into the buffer (these contain bit-field +// size AND fixed point scaling constants) +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +void CBitWrite::WriteBitAngle(float fAngle, int numbits) +{ + int d; + unsigned int mask; + unsigned int shift; + + shift = GetBitForBitnum(numbits); + mask = shift - 1; + + d = (int)((fAngle / 360.0) * shift); + d &= mask; + + WriteUBitLong((unsigned int)d, numbits); +} + +//----------------------------------------------------------------------------- +void CBitWrite::WriteBitCoord(const float f) +{ +#if defined( BB_PROFILING ) + VPROF("CBitWrite::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 CBitWrite::WriteBitCoordMP(const float f, bool bIntegral, bool bLowPrecision) +{ +#if defined( BB_PROFILING ) + VPROF("CBitWrite::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); + } } - this->m_bOverflow = true; + WriteUBitLong(bits, numbits); +} + +//----------------------------------------------------------------------------- +void CBitWrite::WriteBitCellCoord(const float f, int bits, EBitCoordType coordType) +{ +#if defined( BB_PROFILING ) + VPROF("CBitWrite::WriteBitCellCoord"); +#endif + 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::WriteBitVec3Coord(const Vector3D& 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 Vector3D& 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::WriteBitAngles(const QAngle& fa) +{ + // FIXME: + Vector3D tmp(fa.x, fa.y, fa.z); + WriteBitVec3Coord(tmp); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for writing basic integral types into the buffer +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +void CBitWrite::WriteChar(int val) +{ + WriteSBitLong(val, sizeof(char) << 3); +} + +//----------------------------------------------------------------------------- +void CBitWrite::WriteByte(int val) +{ + WriteUBitLong(val, sizeof(unsigned char) << 3); +} + +//----------------------------------------------------------------------------- +void CBitWrite::WriteShort(int val) +{ + WriteSBitLong(val, sizeof(short) << 3); +} + +//----------------------------------------------------------------------------- +void CBitWrite::WriteWord(int val) +{ + WriteUBitLong(val, sizeof(unsigned short) << 3); +} + +//----------------------------------------------------------------------------- +void CBitWrite::WriteLong(long val) +{ + WriteSBitLong(val, sizeof(long) << 3); +} + +//----------------------------------------------------------------------------- +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(long) << 3); + WriteUBitLong(pLongs[*idx], sizeof(long) << 3); +} + +//----------------------------------------------------------------------------- +void CBitWrite::WriteFloat(float val) +{ + // Pre-swap the float, since WriteBits writes raw data + LittleFloat(&val, &val); + + WriteBits(&val, sizeof(val) << 3); +} + +//----------------------------------------------------------------------------- +bool CBitWrite::WriteBytes(const void* pBuf, int nBytes) +{ + return WriteBits(pBuf, nBytes << 3); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for writing strings into the buffer +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +bool CBitWrite::WriteString(const char* pStr) +{ + if (pStr) + { + do + { + WriteChar(*pStr); + ++pStr; + } while (*(pStr - 1) != 0); + } + else + { + WriteChar(0); + } + + return !IsOverflowed(); +} + +//----------------------------------------------------------------------------- +bool CBitWrite::WriteWString(const wchar_t* pStr) +{ + if (pStr) + { + do + { + WriteShort(*pStr); + ++pStr; + } while (*(pStr - 1) != 0); + } + else + { + WriteShort(0); + } + + return !IsOverflowed(); }