bitStream.h
Engine/source/core/stream/bitStream.h
Classes:
class
class
This class acts to provide an "infinitely extending" stream.
class
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#ifndef _BITSTREAM_H_ 25#define _BITSTREAM_H_ 26 27#ifndef _STREAM_H_ 28#include "core/stream/stream.h" 29#endif 30#ifndef _MPOINT3_H_ 31#include "math/mPoint3.h" 32#endif 33#ifndef _CRC_H_ 34#include "core/crc.h" 35#endif 36 37//-------------------------------------- Some caveats when using this class: 38// - Get/setPosition semantics are changed 39// to indicate bit position rather than 40// byte position. 41// 42 43class Point3F; 44class MatrixF; 45class HuffmanProcessor; 46class BitVector; 47class QuatF; 48 49class BitStream : public Stream 50{ 51protected: 52 U8 *dataPtr; 53 S32 bitNum; 54 S32 bufSize; 55 bool error; 56 S32 maxReadBitNum; 57 S32 maxWriteBitNum; 58 char *stringBuffer; 59 Point3F mCompressPoint; 60 61 friend class HuffmanProcessor; 62public: 63 static BitStream *getPacketStream(U32 writeSize = 0); 64 static void sendPacketStream(const NetAddress *addr); 65 66 void setBuffer(void *bufPtr, S32 bufSize, S32 maxSize = 0); 67 U8* getBuffer() { return dataPtr; } 68 U8* getBytePtr(); 69 70 U32 getReadByteSize(); 71 U32 getWriteByteSize(); 72 73 S32 getCurPos() const; 74 void setCurPos(const U32); 75 76 // HACK: We reverted BitStream to this previous version 77 // because it was crashing the build. 78 // 79 // These are just here so that we don't have to revert 80 // the changes from the rest of the code. 81 // 82 // 9/11/2008 - Tom Spilman 83 // 84 S32 getBitPosition() const { return getCurPos(); } 85 void clearStringBuffer(); 86 87 BitStream(void *bufPtr, S32 bufSize, S32 maxWriteSize = -1) { setBuffer(bufPtr, bufSize,maxWriteSize); stringBuffer = NULL; } 88 void clear(); 89 90 void setStringBuffer(char buffer[256]); 91 void writeInt(S32 value, S32 bitCount); 92 S32 readInt(S32 bitCount); 93 94 /// Use this method to write out values in a concise but ass backwards way... 95 /// Good for values you expect to be frequently zero, often small. Worst case 96 /// this will bloat values by nearly 20% (5 extra bits!) Best case you'll get 97 /// one bit (if it's zero). 98 /// 99 /// This is not so much for efficiency's sake, as to make life painful for 100 /// people that want to reverse engineer our network or file formats. 101 void writeCussedU32(U32 val) 102 { 103 // Is it zero? 104 if(writeFlag(val == 0)) 105 return; 106 107 if(writeFlag(val <= 0xF)) // 4 bit 108 writeRangedU32(val, 0, 0xF); 109 else if(writeFlag(val <= 0xFF)) // 8 bit 110 writeRangedU32(val, 0, 0xFF); 111 else if(writeFlag(val <= 0xFFFF)) // 16 bit 112 writeRangedU32(val, 0, 0xFFFF); 113 else if(writeFlag(val <= 0xFFFFFF)) // 24 bit 114 writeRangedU32(val, 0, 0xFFFFFF); 115 else 116 writeRangedU32(val, 0, 0xFFFFFFFF); 117 } 118 119 U32 readCussedU32() 120 { 121 if(readFlag()) 122 return 0; 123 124 if(readFlag()) 125 return readRangedU32(0, 0xF); 126 else if(readFlag()) 127 return readRangedU32(0, 0xFF); 128 else if(readFlag()) 129 return readRangedU32(0, 0xFFFF); 130 else if(readFlag()) 131 return readRangedU32(0, 0xFFFFFF); 132 else 133 return readRangedU32(0, 0xFFFFFFFF); 134 } 135 136 void writeSignedInt(S32 value, S32 bitCount); 137 S32 readSignedInt(S32 bitCount); 138 139 void writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd); 140 U32 readRangedU32(U32 rangeStart, U32 rangeEnd); 141 142 /// Writes a clamped signed integer to the stream using 143 /// an optimal amount of bits for the range. 144 void writeRangedS32( S32 value, S32 min, S32 max ); 145 146 /// Reads a ranged signed integer written with writeRangedS32. 147 S32 readRangedS32( S32 min, S32 max ); 148 149 // read and write floats... floats are 0 to 1 inclusive, signed floats are -1 to 1 inclusive 150 151 F32 readFloat(S32 bitCount); 152 F32 readSignedFloat(S32 bitCount); 153 154 void writeFloat(F32 f, S32 bitCount); 155 void writeSignedFloat(F32 f, S32 bitCount); 156 157 /// Writes a clamped floating point value to the 158 /// stream with the desired bits of precision. 159 void writeRangedF32( F32 value, F32 min, F32 max, U32 numBits ); 160 161 /// Reads a ranged floating point value written with writeRangedF32. 162 F32 readRangedF32( F32 min, F32 max, U32 numBits ); 163 164 void writeClassId(U32 classId, U32 classType, U32 classGroup); 165 S32 readClassId(U32 classType, U32 classGroup); // returns -1 if the class type is out of range 166 167 // writes a normalized vector 168 void writeNormalVector(const Point3F& vec, S32 bitCount); 169 void readNormalVector(Point3F *vec, S32 bitCount); 170 171 void clearCompressionPoint(); 172 void setCompressionPoint(const Point3F& p); 173 174 // Matching calls to these compression methods must, of course, 175 // have matching scale values. 176 void writeCompressedPoint(const Point3F& p,F32 scale = 0.001f); 177 void readCompressedPoint(Point3F* p,F32 scale = 0.001f); 178 179 // Uses the above method to reduce the precision of a normal vector so the server can 180 // determine exactly what is on the client. (Pre-dumbing the vector before sending 181 // to the client can result in precision errors...) 182 static Point3F dumbDownNormal(const Point3F& vec, S32 bitCount); 183 184 /// Writes a compressed vector as separate magnitude and 185 /// normal components. The final space used depends on the 186 /// content of the vector. 187 /// 188 /// - 1 bit is used to skip over zero length vectors. 189 /// - 1 bit is used to mark if the magnitude exceeds max. 190 /// - The magnitude as: 191 /// a. magBits if less than maxMag. 192 /// b. a full 32bit value if greater than maxMag. 193 /// - The normal as a phi and theta sized normalBits+1 and normalBits. 194 /// 195 void writeVector( Point3F vec, F32 maxMag, S32 magBits, S32 normalBits ); 196 197 /// Reads a compressed vector. 198 /// @see writeVector 199 void readVector( Point3F *outVec, F32 maxMag, S32 magBits, S32 normalBits ); 200 201 // writes an affine transform (full precision version) 202 void writeAffineTransform(const MatrixF&); 203 void readAffineTransform(MatrixF*); 204 205 /// Writes a quaternion in a lossy compressed format that 206 /// is ( bitCount * 3 ) + 1 bits in size. 207 /// 208 /// @param quat The normalized quaternion to write. 209 /// @param bitCount The the storage space for the xyz component of 210 /// the quaternion. 211 /// 212 void writeQuat( const QuatF& quat, U32 bitCount = 9 ); 213 214 /// Reads a quaternion written with writeQuat. 215 /// 216 /// @param quat The normalized quaternion to write. 217 /// @param bitCount The the storage space for the xyz component of 218 /// the quaternion. Must match the bitCount at write. 219 /// @see writeQuat 220 /// 221 void readQuat( QuatF *outQuat, U32 bitCount = 9 ); 222 223 virtual void writeBits(S32 bitCount, const void *bitPtr); 224 virtual void readBits(S32 bitCount, void *bitPtr); 225 virtual bool writeFlag(bool val); 226 227 inline bool writeFlag(U32 val) 228 { 229 return writeFlag(val != 0); 230 } 231 232 inline bool writeFlag(void *val) 233 { 234 return writeFlag(val != 0); 235 } 236 237 virtual bool readFlag(); 238 239 void writeBits(const BitVector &bitvec); 240 void readBits(BitVector *bitvec); 241 242 void setBit(S32 bitCount, bool set); 243 bool testBit(S32 bitCount); 244 245 bool isFull() { return bitNum > (bufSize << 3); } 246 bool isValid() { return !error; } 247 248 bool _read (const U32 size,void* d); 249 bool _write(const U32 size,const void* d); 250 251 void readString(char stringBuf[256]); 252 void writeString(const char *stringBuf, S32 maxLen=255); 253 254 bool hasCapability(const Capability) const { return true; } 255 U32 getPosition() const; 256 bool setPosition(const U32 in_newPosition); 257 U32 getStreamSize(); 258}; 259 260class ResizeBitStream : public BitStream 261{ 262protected: 263 U32 mMinSpace; 264public: 265 ResizeBitStream(U32 minSpace = 1500, U32 initialSize = 0); 266 void validate(); 267 ~ResizeBitStream(); 268}; 269 270/// This class acts to provide an "infinitely extending" stream. 271/// 272/// Basically, it does what ResizeBitStream does, but it validates 273/// on every write op, so that you never have to worry about overwriting 274/// the buffer. 275class InfiniteBitStream : public ResizeBitStream 276{ 277public: 278 InfiniteBitStream(); 279 ~InfiniteBitStream(); 280 281 /// Ensure we have space for at least upcomingBytes more bytes in the stream. 282 void validate(U32 upcomingBytes); 283 284 /// Reset the stream to zero length (but don't clean memory). 285 void reset(); 286 287 /// Shrink the buffer down to match the actual size of the data. 288 void compact(); 289 290 /// Write us out to a stream... Results in last byte getting padded! 291 void writeToStream(Stream &s); 292 293 virtual void writeBits(S32 bitCount, const void *bitPtr) 294 { 295 validate((bitCount >> 3) + 1); // Add a little safety. 296 BitStream::writeBits(bitCount, bitPtr); 297 } 298 299 virtual bool writeFlag(bool val) 300 { 301 validate(1); // One bit will at most grow our buffer by a byte. 302 return BitStream::writeFlag(val); 303 } 304 305 const U32 getCRC() 306 { 307 // This could be kinda inefficient - BJG 308 return CRC::calculateCRC(getBuffer(), getStreamSize()); 309 } 310}; 311 312//------------------------------------------------------------------------------ 313//-------------------------------------- INLINES 314// 315inline S32 BitStream::getCurPos() const 316{ 317 return bitNum; 318} 319 320inline void BitStream::setCurPos(const U32 in_position) 321{ 322 AssertFatal(in_position < (U32)(bufSize << 3), "Out of range bitposition"); 323 bitNum = S32(in_position); 324} 325 326inline bool BitStream::readFlag() 327{ 328 if(bitNum > maxReadBitNum) 329 { 330 error = true; 331 AssertFatal(false, "Out of range read"); 332 return false; 333 } 334 S32 mask = 1 << (bitNum & 0x7); 335 bool ret = (*(dataPtr + (bitNum >> 3)) & mask) != 0; 336 bitNum++; 337 return ret; 338} 339 340inline void BitStream::writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd) 341{ 342 AssertFatal(value >= rangeStart && value <= rangeEnd, "Out of bounds value!"); 343 AssertFatal(rangeEnd >= rangeStart, "error, end of range less than start"); 344 345 U32 rangeSize = rangeEnd - rangeStart + 1; 346 U32 rangeBits = getBinLog2(getNextPow2(rangeSize)); 347 348 writeInt(S32(value - rangeStart), S32(rangeBits)); 349} 350 351inline U32 BitStream::readRangedU32(U32 rangeStart, U32 rangeEnd) 352{ 353 AssertFatal(rangeEnd >= rangeStart, "error, end of range less than start"); 354 355 U32 rangeSize = rangeEnd - rangeStart + 1; 356 U32 rangeBits = getBinLog2(getNextPow2(rangeSize)); 357 358 U32 val = U32(readInt(S32(rangeBits))); 359 return val + rangeStart; 360} 361 362inline void BitStream::writeRangedS32( S32 value, S32 min, S32 max ) 363{ 364 value = mClamp( value, min, max ); 365 writeRangedU32( ( value - min ), 0, ( max - min ) ); 366} 367 368inline S32 BitStream::readRangedS32( S32 min, S32 max ) 369{ 370 return readRangedU32( 0, ( max - min ) ) + min; 371} 372 373inline void BitStream::writeRangedF32( F32 value, F32 min, F32 max, U32 numBits ) 374{ 375 value = ( mClampF( value, min, max ) - min ) / ( max - min ); 376 writeInt( (S32)mFloor(value * F32( (1 << numBits) - 1 )), numBits ); 377} 378 379inline F32 BitStream::readRangedF32( F32 min, F32 max, U32 numBits ) 380{ 381 F32 value = (F32)readInt( numBits ); 382 value /= F32( ( 1 << numBits ) - 1 ); 383 return min + value * ( max - min ); 384} 385 386#endif //_BITSTREAM_H_ 387
