//===== 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. 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(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 //----------------------------------------------------------------------------- class CBitBuffer { public: CBitBuffer(void); inline void SetDebugName(const char* pName) { m_pDebugName = pName; } inline const char* GetDebugName() const { return m_pDebugName; } inline bool IsOverflowed() const { return m_bOverflow; } inline void SetOverflowFlag() { m_bOverflow = true; } //////////////////////////////////// const char* m_pDebugName; uint8_t m_bOverflow; int64_t m_nDataBits; size_t m_nDataBytes; }; 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(); int ReadSBitLong(int numbits); uint32 ReadUBitLong(int numbits); inline int ReadByte() { return ReadSBitLong(sizeof(unsigned char) << 3); } inline int ReadChar() { return ReadSBitLong(sizeof(char) << 3); } bool ReadString(char* pStr, int bufLen, bool bLine = false, int* pOutNumChars = nullptr); //////////////////////////////////// uint32 m_nInBufWord; int m_nBitsAvail; const uint32* m_pDataIn; const uint32* m_pBufferEnd; const uint32* m_pData; }; class bf_read : public CBitRead { 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. 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); // 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); inline bool WriteBytes(const void* pIn, int nBytes) { return WriteBits(pIn, nBytes << 3); } // How many bytes are filled in? inline int GetNumBytesWritten() const { return BitByte(this->m_iCurBit); } inline int GetNumBitsWritten() const { return this->m_iCurBit; } inline int GetMaxNumBits() const { return this->m_nDataBits; } inline int GetNumBitsLeft() const { return this->m_nDataBits - m_iCurBit; } inline int GetNumBytesLeft() const { return this->GetNumBitsLeft() >> 3; } inline unsigned char* GetData() const { return this->m_pData; } inline const char* GetDebugName() const { return this->m_pDebugName; } inline void SetDebugName(const char* pDebugName) { m_pDebugName = pDebugName; } // Has the buffer overflowed? bool CheckForOverflow(int nBits); void SetOverflowFlag(); inline bool IsOverflowed() const { return this->m_bOverflow; } private: // 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