From 6a69965b3373ad6894d3247aa65d29ec457c5415 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Mon, 10 Apr 2023 15:02:56 +0200 Subject: [PATCH] Improve 'bf_read' and 'bf_write' * Implemented a proper initializer for the mask tables (s_nMaskTable has been removed). * Implemented write methods in 'bf_write'. There are still some missing methods; there have to be added in later. * Documented most functions. --- r5dev/public/tier1/bitbuf.h | 156 +++++++- r5dev/tier1/bitbuf.cpp | 737 ++++++++++++++++++++++++++++-------- 2 files changed, 722 insertions(+), 171 deletions(-) diff --git a/r5dev/public/tier1/bitbuf.h b/r5dev/public/tier1/bitbuf.h index a7b23ae1..da5ea3e3 100644 --- a/r5dev/public/tier1/bitbuf.h +++ b/r5dev/public/tier1/bitbuf.h @@ -1,6 +1,22 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: buffer serialization/deserialization. +// +// $NoKeywords: $ +// NOTE: bf_read is guaranteed to return zeros if it overflows. +//===========================================================================// + #ifndef BITBUF_H #define BITBUF_H +//----------------------------------------------------------------------------- +// 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. @@ -8,8 +24,82 @@ typedef enum BITBUFERROR_NUM_ERRORS } BitBufErrorType; +typedef void (*BitBufErrorHandler)(BitBufErrorType errorType, const char* pDebugName); -#define LittleDWord(val) (val) +#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(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(n & 1); + } + + const int kMaxVarintBytes = 10; + const int kMaxVarint32Bytes = 5; +} //----------------------------------------------------------------------------- // Used for serialization @@ -33,6 +123,15 @@ public: 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(void) : CBitBuffer() + { + } + + void StartReading(const void* pData, size_t nBytes, int64 iStartBit = 0, int64 nBits = -1); + bool Seek(int64 nPosition); + void GrabNextDWord(bool bOverFlowImmediately = false); void FetchNext(); @@ -43,11 +142,8 @@ public: int ReadChar(); bool ReadString(char* pStr, int bufLen, bool bLine = false, int* pOutNumChars = nullptr); - void StartReading(const void* pData, size_t nBytes, int64_t iStartBit = 0, int64_t nBits = -1); - bool Seek(int64_t nPosition); - //////////////////////////////////// - uint32_t m_nInBufWord; + uint32 m_nInBufWord; int m_nBitsAvail; const uint32* m_pDataIn; const uint32* m_pBufferEnd; @@ -63,6 +159,35 @@ public: struct 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); + + // Restart buffer writing. + void Reset(); + void SeekToBit(int 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); + + // 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); + + // 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); + // How many bytes are filled in? int GetNumBytesWritten() const; int GetNumBitsWritten() const; @@ -70,19 +195,26 @@ public: int GetNumBitsLeft() const; int GetNumBytesLeft() const; unsigned char* GetData() const; + const char* GetDebugName() const; + void SetDebugName(const char* pDebugName); // Has the buffer overflowed? bool CheckForOverflow(int nBits); bool IsOverflowed() const; void SetOverflowFlag(); private: - uint8_t* m_pData; - int m_nDataBytes; - int m_nDataBits; - int m_iCurBit; - bool m_bOverflow; - bool m_bAssertOnOverflow; - const char* m_pDebugName; + // The current buffer. + unsigned char* m_pData; + int m_nDataBytes; + int m_nDataBits; + + // Where we are in the buffer. + int m_iCurBit; + + // Errors? + bool m_bOverflow; + bool m_bAssertOnOverflow; + const char* m_pDebugName; }; #endif // BITBUF_H diff --git a/r5dev/tier1/bitbuf.cpp b/r5dev/tier1/bitbuf.cpp index 23554cf9..4ad11297 100644 --- a/r5dev/tier1/bitbuf.cpp +++ b/r5dev/tier1/bitbuf.cpp @@ -1,50 +1,71 @@ -//===========================================================================// +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// // -// Purpose: +// Purpose: buffer serialization/deserialization. // // $NoKeywords: $ //===========================================================================// #include "core/stdafx.h" #include "tier1/bitbuf.h" +#include "mathlib/swap.h" +#include "mathlib/bitvec.h" -const uint32 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, + +//----------------------------------------------------------------------------- +// Write masks +//----------------------------------------------------------------------------- +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; + m_BitWriteMasks[startbit][nBitsLeft] = GetBitForBitnum(int(startbit)) - 1; + if (endbit < 32) + m_BitWriteMasks[startbit][nBitsLeft] |= ~(GetBitForBitnum(int(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); + } + + // 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_ExtraMasks[33]; // (1 << i) - 1 + unsigned long m_LittleBits[32]; }; +static CBitWriteMasksInit s_BitWriteMasks; +//----------------------------------------------------------------------------- +// Error handler +//----------------------------------------------------------------------------- +static BitBufErrorHandler g_BitBufErrorHandler = 0; +void InternalBitBufErrorHandler(BitBufErrorType errorType, const char* pDebugName) +{ + if (g_BitBufErrorHandler) + g_BitBufErrorHandler(errorType, pDebugName); +} +void SetBitBufErrorHandler(BitBufErrorHandler fn) +{ + g_BitBufErrorHandler = fn; +} + +// ---------------------------------------------------------------------------------------- // +// CBitBuffer +// ---------------------------------------------------------------------------------------- // + CBitBuffer::CBitBuffer(void) { m_bOverflow = false; @@ -73,123 +94,51 @@ void CBitBuffer::SetOverflowFlag() m_bOverflow = true; } -void CBitRead::GrabNextDWord(bool bOverFlowImmediately) +// ---------------------------------------------------------------------------------------- // +// bf_read +// ---------------------------------------------------------------------------------------- // +CBitRead::CBitRead(const void* pData, int nBytes, int nBits /*= -1*/) { - if (m_pDataIn == m_pBufferEnd) + StartReading(pData, nBytes, 0, nBits); +} + +CBitRead::CBitRead(const char* pDebugName, const void* pData, int nBytes, int nBits /*= -1*/) +{ + SetDebugName(pDebugName); + StartReading(pData, nBytes, 0, nBits); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBitRead::StartReading(const void* pData, size_t nBytes, int64 iStartBit, int64 nBits) +{ + // Make sure it's dword aligned and padded. + assert((int64(pData) & 3) == 0); + m_pData = (uint32*)pData; + m_pDataIn = m_pData; + m_nDataBytes = nBytes; + + if (nBits == -1) { - m_nBitsAvail = 1; - m_nInBufWord = 0; - - m_pDataIn++; - - if (bOverFlowImmediately) - SetOverflowFlag(); + m_nDataBits = nBytes << 3; } 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++)); - } + assert(nBits <= int64(nBytes * 8)); + m_nDataBits = nBits; } + m_bOverflow = false; + m_pBufferEnd = reinterpret_cast (reinterpret_cast (m_pData) + nBytes); + if (m_pData) + Seek(iStartBit); + } -void CBitRead::FetchNext() -{ - m_nBitsAvail = 32; - GrabNextDWord(false); -} - -uint32 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; - } -} - -int CBitRead::ReadSBitLong(int numbits) -{ - int nRet = ReadUBitLong(numbits); - return (nRet << (32 - numbits)) >> (32 - numbits); -} - -int CBitRead::ReadByte() -{ - return ReadSBitLong(sizeof(unsigned char) << 3); -} - -int CBitRead::ReadChar() -{ - return ReadSBitLong(sizeof(char) << 3); -} - -bool CBitRead::ReadString(char* pStr, int maxLen, bool bLine, int* pOutNumChars) -{ - assert(maxLen != 0); - - bool bTooSmall = false; - int iChar = 0; - while (1) - { - char val = char(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. - pStr[iChar] = '\0'; - - if (pOutNumChars) - *pOutNumChars = iChar; - - return !IsOverflowed() && !bTooSmall; -} - -bool CBitRead::Seek(int64_t nPosition) +//----------------------------------------------------------------------------- +// Purpose: seeks to a specific position in the buffer +//----------------------------------------------------------------------------- +bool CBitRead::Seek(int64 nPosition) { bool bSucc = true; if (nPosition < 0 || nPosition > m_nDataBits) @@ -199,7 +148,7 @@ bool CBitRead::Seek(int64_t nPosition) nPosition = m_nDataBits; } size_t nHead = m_nDataBytes & 3; // non-multiple-of-4 bytes at head of buffer. We put the "round off" - // at the head to make reading and detecting the end efficient. + // at the head to make reading and detecting the end efficient. size_t nByteOfs = nPosition / 8; if ((m_nDataBytes < 4) || (nHead && (nByteOfs < nHead))) @@ -239,12 +188,202 @@ bool CBitRead::Seek(int64_t nPosition) return bSucc; } -void CBitRead::StartReading(const void* pData, size_t nBytes, int64_t iStartBit, int64_t nBits) +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +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++)); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBitRead::FetchNext() +{ + m_nBitsAvail = 32; + GrabNextDWord(false); +} + +//----------------------------------------------------------------------------- +// Purpose: reads an unsigned integer from the buffer +//----------------------------------------------------------------------------- +uint32 CBitRead::ReadUBitLong(int numbits) +{ + if (m_nBitsAvail >= numbits) + { + unsigned int nRet = m_nInBufWord & s_BitWriteMasks.m_ExtraMasks[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_BitWriteMasks.m_ExtraMasks[numbits]) << m_nBitsAvail); + m_nBitsAvail = 32 - numbits; + m_nInBufWord >>= numbits; + + return nRet; + } +} + +//----------------------------------------------------------------------------- +// Purpose: reads a signed integer from the buffer +//----------------------------------------------------------------------------- +int CBitRead::ReadSBitLong(int numbits) +{ + int nRet = ReadUBitLong(numbits); + return (nRet << (32 - numbits)) >> (32 - numbits); +} + +//----------------------------------------------------------------------------- +// Purpose: reads byte from the buffer +//----------------------------------------------------------------------------- +int CBitRead::ReadByte() +{ + return ReadSBitLong(sizeof(unsigned char) << 3); +} + +//----------------------------------------------------------------------------- +// Purpose: reads character from the buffer +//----------------------------------------------------------------------------- +int CBitRead::ReadChar() +{ + return ReadSBitLong(sizeof(char) << 3); +} + +//----------------------------------------------------------------------------- +// Purpose: reads a string from the buffer +//----------------------------------------------------------------------------- +bool CBitRead::ReadString(char* pStr, int maxLen, bool bLine, int* pOutNumChars) +{ + assert(maxLen != 0); + + bool bTooSmall = false; + int iChar = 0; + while (1) + { + char val = char(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. + pStr[iChar] = '\0'; + + if (pOutNumChars) + *pOutNumChars = iChar; + + return !IsOverflowed() && !bTooSmall; +} + +// ---------------------------------------------------------------------------------------- // +// bf_write +// ---------------------------------------------------------------------------------------- // +bf_write::bf_write() +{ + //DEBUG_LINK_CHECK; + 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) +{ + //DEBUG_LINK_CHECK; + 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); +} + +//----------------------------------------------------------------------------- +// Purpose: resets buffer writing +//----------------------------------------------------------------------------- +void bf_write::Reset() +{ + m_iCurBit = 0; + m_bOverflow = false; +} + +//----------------------------------------------------------------------------- +// Purpose: seeks to a specific bit +//----------------------------------------------------------------------------- +void bf_write::SeekToBit(int bitPos) +{ + m_iCurBit = bitPos; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void bf_write::StartWriting(void* pData, int nBytes, int iStartBit, int nBits) { // Make sure it's dword aligned and padded. - assert((int64_t(pData) & 3) == 0); - m_pData = (uint32*)pData; - m_pDataIn = m_pData; + //DEBUG_LINK_CHECK; + Assert((nBytes % 4) == 0); + Assert(((uintp)pData & 3) == 0); + + // The writing code will overrun the end of the buffer if it isn't dword aligned, so truncate to force alignment + nBytes &= ~3; + + m_pData = (unsigned char*)pData; m_nDataBytes = nBytes; if (nBits == -1) @@ -253,62 +392,339 @@ void CBitRead::StartReading(const void* pData, size_t nBytes, int64_t iStartBit, } else { - assert(nBits <= int64_t(nBytes * 8)); + Assert(nBits <= nBytes * 8); m_nDataBits = nBits; } + + m_iCurBit = iStartBit; m_bOverflow = false; - m_pBufferEnd = reinterpret_cast (reinterpret_cast (m_pData) + nBytes); - if (m_pData) - Seek(iStartBit); - } -inline int BitByte(int bits) +//----------------------------------------------------------------------------- +// Purpose: writes a bit to the buffer without checking for overflow +//----------------------------------------------------------------------------- +inline void bf_write::WriteOneBitNoCheck(int nValue) { - // return PAD_NUMBER( bits, 8 ) >> 3; - return (bits + 7) >> 3; + 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 bf_write::WriteOneBit(int nValue) +{ + if (!CheckForOverflow(1)) + WriteOneBitNoCheck(nValue); +} + +//----------------------------------------------------------------------------- +// Purpose: writes a bit to the buffer at a specific bit index +//----------------------------------------------------------------------------- +inline void bf_write::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)); +} + +//----------------------------------------------------------------------------- +// Purpose: writes an unsigned integer to the buffer +//----------------------------------------------------------------------------- +/*BITBUF_INLINE*/ void bf_write::WriteUBitLong(unsigned int curData, int numbits, bool bCheckRange) +{ +#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 + + // Bounds checking.. + if ((m_iCurBit + numbits) > m_nDataBits) + { + m_iCurBit = m_nDataBits; + SetOverflowFlag(); + CallErrorHandler(BITBUFERROR_BUFFER_OVERRUN, GetDebugName()); + return; + } + + int nBitsLeft = numbits; + int iCurBit = m_iCurBit; + + // Mask in a dword. + unsigned int iDWord = iCurBit >> 5; + Assert((iDWord * 4 + sizeof(int32)) <= (unsigned int)m_nDataBytes); + + uint32 iCurBitMasked = iCurBit & 31; + + uint32 dword = LoadLittleDWord((uint32*)m_pData, iDWord); + + dword &= s_BitWriteMasks.m_BitWriteMasks[iCurBitMasked][nBitsLeft]; + dword |= curData << iCurBitMasked; + + // write to stream (lsb to msb) properly + StoreLittleDWord((uint32*)m_pData, iDWord, dword); + + // Did it span a dword? + int nBitsWritten = 32 - iCurBitMasked; + if (nBitsWritten < nBitsLeft) + { + nBitsLeft -= nBitsWritten; + curData >>= nBitsWritten; + + // read from stream (lsb to msb) properly + dword = LoadLittleDWord((uint32*)m_pData, iDWord + 1); + + dword &= s_BitWriteMasks.m_BitWriteMasks[0][nBitsLeft]; + dword |= curData; + + // write to stream (lsb to msb) properly + StoreLittleDWord((uint32*)m_pData, iDWord + 1, dword); + } + + m_iCurBit += numbits; +} + +//----------------------------------------------------------------------------- +// Purpose: writes a signed integer to the buffer +// (Sign bit comes first) +//----------------------------------------------------------------------------- +void bf_write::WriteSBitLong(int data, int numbits) +{ + // Do we have a valid # of bits to encode with? + Assert(numbits >= 1); + + // Note: it does this wierdness here so it's bit-compatible with regular integer data in the buffer. + // (Some old code writes direct integers right into the buffer). + if (data < 0) + { +#ifdef _DEBUG + if (numbits < 32) + { + // Make sure it doesn't overflow. + + if (data < 0) + { + Assert(data >= -(BitForBitnum(numbits - 1))); + } + else + { + Assert(data < (BitForBitnum(numbits - 1))); + } + } +#endif + + WriteUBitLong((unsigned int)(0x80000000 + data), numbits - 1, false); + WriteOneBit(1); + } + else + { + WriteUBitLong((unsigned int)data, numbits - 1); + WriteOneBit(0); + } +} +//----------------------------------------------------------------------------- +// Purpose: writes a signed or unsigned integer to the buffer +//----------------------------------------------------------------------------- +void bf_write::WriteBitLong(unsigned int data, int numbits, bool bSigned) +{ + if (bSigned) + WriteSBitLong((int)data, numbits); + else + WriteUBitLong(data, numbits); +} + +//----------------------------------------------------------------------------- +// Purpose: writes a list of bits to the buffer +//----------------------------------------------------------------------------- +bool bf_write::WriteBits(const void* pInData, int nBits) +{ +#if defined( BB_PROFILING ) + VPROF("bf_write::WriteBits"); +#endif + + unsigned char* pIn = (unsigned char*)pInData; + int nBitsLeft = nBits; + + // Bounds checking.. + if ((m_iCurBit + nBits) > m_nDataBits) + { + SetOverflowFlag(); + CallErrorHandler(BITBUFERROR_BUFFER_OVERRUN, GetDebugName()); + return false; + } + + // Align input to dword boundary + while (((uintp)pIn & 3) != 0 && nBitsLeft >= 8) + { + WriteUBitLong(*pIn, 8, false); + ++pIn; + nBitsLeft -= 8; + } + + if (nBitsLeft >= 32) + { + if ((m_iCurBit & 7) == 0) + { + // current bit is byte aligned, do block copy + int numbytes = nBitsLeft >> 3; + int numbits = numbytes << 3; + + memcpy(m_pData + (m_iCurBit >> 3), pIn, numbytes); + pIn += numbytes; + nBitsLeft -= numbits; + m_iCurBit += numbits; + } + else + { + const uint32 iBitsRight = (m_iCurBit & 31); + Assert(iBitsRight > 0); // should not be aligned, otherwise it would have been handled before + const uint32 iBitsLeft = 32 - iBitsRight; + const int iBitsChanging = 32 + iBitsLeft; // how many bits are changed during one step (not necessary written meaningful) + 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 + + // copy in DWORD blocks + while (nBitsLeft >= iBitsChanging) + { + uint32 curData = LittleDWord(*(uint32*)pIn); + pIn += sizeof(uint32); + + outWord |= curData << iBitsRight; + StoreLittleDWord((uint32*)m_pData, iDWord, outWord); + + ++iDWord; + outWord = curData >> iBitsLeft; + + nBitsLeft -= 32; + m_iCurBit += 32; + } + + // store last word + StoreLittleDWord((uint32*)m_pData, iDWord, outWord); + + // write remaining DWORD + if (nBitsLeft >= 32) + { + WriteUBitLong(LittleDWord(*((uint32*)pIn)), 32, false); + pIn += sizeof(uint32); + nBitsLeft -= 32; + } + } + } + + // write remaining bytes + while (nBitsLeft >= 8) + { + WriteUBitLong(*pIn, 8, false); + ++pIn; + nBitsLeft -= 8; + } + + // write remaining bits + if (nBitsLeft) + { + WriteUBitLong(*pIn, nBitsLeft, false); + } + + return !IsOverflowed(); +} + +//----------------------------------------------------------------------------- +// Purpose: writes a bit into the buffer +//----------------------------------------------------------------------------- bool bf_write::IsOverflowed() const { return this->m_bOverflow; } +//----------------------------------------------------------------------------- +// Purpose: returns the number of bytes written to the buffer +//----------------------------------------------------------------------------- int bf_write::GetNumBytesWritten() const { return BitByte(this->m_iCurBit); } +//----------------------------------------------------------------------------- +// Purpose: returns the number of bits written to the buffer +//----------------------------------------------------------------------------- int bf_write::GetNumBitsWritten() const { return this->m_iCurBit; } +//----------------------------------------------------------------------------- +// Purpose: returns the number of bits in the buffer +//----------------------------------------------------------------------------- int bf_write::GetMaxNumBits() const { return this->m_nDataBits; } +//----------------------------------------------------------------------------- +// Purpose: returns the number of bits left in the buffer +//----------------------------------------------------------------------------- int bf_write::GetNumBitsLeft() const { return this->m_nDataBits - m_iCurBit; } +//----------------------------------------------------------------------------- +// Purpose: returns the number of bytes left in the buffer +//----------------------------------------------------------------------------- int bf_write::GetNumBytesLeft() const { return this->GetNumBitsLeft() >> 3; } +//----------------------------------------------------------------------------- +// Purpose: returns the data pointer +//----------------------------------------------------------------------------- unsigned char* bf_write::GetData() const { return this->m_pData; } +//----------------------------------------------------------------------------- +// Purpose: returns the debug name +//----------------------------------------------------------------------------- const char* bf_write::GetDebugName() const { return this->m_pDebugName; } +//----------------------------------------------------------------------------- +// Purpose: sets the debug name +//----------------------------------------------------------------------------- +void bf_write::SetDebugName(const char* pDebugName) +{ + m_pDebugName = pDebugName; +} + +//----------------------------------------------------------------------------- +// Purpose: checks if we have enough space for the requested number of bits +//----------------------------------------------------------------------------- bool bf_write::CheckForOverflow(int nBits) { if (this->m_iCurBit + nBits > this->m_nDataBits) @@ -319,6 +735,9 @@ bool bf_write::CheckForOverflow(int nBits) return this->m_bOverflow; } +//----------------------------------------------------------------------------- +// Purpose: sets the overflow flag +//----------------------------------------------------------------------------- void bf_write::SetOverflowFlag() { if (this->m_bAssertOnOverflow) @@ -327,4 +746,4 @@ void bf_write::SetOverflowFlag() } this->m_bOverflow = true; -} \ No newline at end of file +}