Blame |
Last modification |
View Log
| RSS feed
#include "Buffer.h"
#include "Debug.h"
#define POLYNOMIAL 0x04c11db7L
static BOOL bCrcTableGenerated = FALSE;
static unsigned long crc_table[256];
using namespace Sexy;
using namespace std;
static char* gWebEncodeMap = ".-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static int gWebDecodeMap[256] =
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 0, -1, 1, 0, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1
, -1, -1, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29
, 30, 31, 32, 33, 34, 35, 36, 37, -1, -1, -1, -1, -1, -1, 38, 39, 40, 41, 42, 43
, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
//----------------------------------------------------------------------------
// Generate the table of CRC remainders for all possible bytes.
//----------------------------------------------------------------------------
static void GenerateCRCTable(void)
{
bCrcTableGenerated = TRUE;
register int i, j;
register unsigned long crc_accum;
for (i = 0; i < 256; i++)
{
crc_accum = ((unsigned long) i << 24);
for ( j = 0; j < 8; j++ )
{
if (crc_accum & 0x80000000L)
crc_accum = (crc_accum << 1) ^ POLYNOMIAL;
else
crc_accum = (crc_accum << 1);
}
crc_table[i] = crc_accum;
}
}
//----------------------------------------------------------------------------
// Update the CRC on the data block one byte at a time.
//----------------------------------------------------------------------------
static unsigned long UpdateCRC(unsigned long crc_accum,
const char *data_blk_ptr,
int data_blk_size)
{
if (!bCrcTableGenerated)
GenerateCRCTable();
register int i, j;
for (j = 0; j < data_blk_size; j++)
{
i = ((int) (crc_accum >> 24) ^ *data_blk_ptr++) & 0xff;
crc_accum = (crc_accum << 8) ^ crc_table[i];
}
return crc_accum;
}
//----------------------------------------------------------------------------
// Stream UTF8 data in a const char* to keep receiving wchar_ts
//----------------------------------------------------------------------------
static int GetUTF8Char(const char** theBuffer, int theLen, wchar_t* theChar)
{
static const unsigned short aMaskData[] = {
0xC0, // 1 extra byte
0xE0, // 2 extra bytes
0xF0, // 3 extra bytes
0xF8, // 4 extra bytes
0xFC // 5 extra bytes
};
if (theLen == 0) return 0;
const char* aBuffer = *theBuffer;
int aTempChar = int((unsigned char)*aBuffer++);
if ((aTempChar & 0x80) != 0)
{
if ((aTempChar & 0xC0) != 0xC0) return 0; // sanity check: high bit should not be set without the next highest bit being set, too.
int aBytesRead[6];
int* aBytesReadPtr = &aBytesRead[0];
*aBytesReadPtr++ = aTempChar;
int aLen;
for (aLen = 0; aLen < (int)(sizeof(aMaskData)/sizeof(*aMaskData)); ++aLen)
{
if ( (aTempChar & aMaskData[aLen]) == ((aMaskData[aLen] << 1) & aMaskData[aLen]) ) break;
}
if (aLen >= (int)(sizeof(aMaskData)/sizeof(*aMaskData))) return 0;
aTempChar &= ~aMaskData[aLen];
int aTotalLen = aLen+1;
if (aTotalLen < 2 || aTotalLen > 6) return 0;
int anExtraChar = 0;
while (aLen > 0 && (aBuffer - *theBuffer) < theLen)
{
anExtraChar = int((unsigned char)*aBuffer++);
if ((anExtraChar & 0xC0) != 0x80) return 0; // sanity check: high bit set, and next highest bit NOT set.
*aBytesReadPtr++ = anExtraChar;
aTempChar = (aTempChar << 6) | (anExtraChar & 0x3F);
--aLen;
}
if (aLen > 0) return 0; // ran out of data before ending sequence
// validate substrings
bool valid = true;
switch (aTotalLen)
{
case 2:
valid = !((aBytesRead[0] & 0x3E) == 0);
break;
case 3:
valid = !((aBytesRead[0] & 0x1F) == 0 && (aBytesRead[1] & 0x20) == 0);
break;
case 4:
valid = !((aBytesRead[0] & 0x0F) == 0 && (aBytesRead[1] & 0x30) == 0);
break;
case 5:
valid = !((aBytesRead[0] & 0x07) == 0 && (aBytesRead[1] & 0x38) == 0);
break;
case 6:
valid = !((aBytesRead[0] & 0x03) == 0 && (aBytesRead[1] & 0x3C) == 0);
break;
}
if (!valid) return 0;
}
int aConsumedCount = aBuffer - *theBuffer;
if ( (aTempChar >= 0xD800 && aTempChar <= 0xDFFF) || (aTempChar >= 0xFFFE && aTempChar <= 0xFFFF) )
return 0;
*theChar = (wchar_t)aTempChar;
*theBuffer = aBuffer;
return aConsumedCount;
}
Buffer::Buffer()
{
mDataBitSize = 0;
mReadBitPos = 0;
mWriteBitPos = 0;
}
Buffer::~Buffer()
{
}
std::string Buffer::ToWebString() const
{
std::string aString;
int aSizeBits = mWriteBitPos;
int anOldReadBitPos = mReadBitPos;
mReadBitPos = 0;
char aStr[256];
sprintf(aStr, "%08X", aSizeBits);
aString += aStr;
int aNumChars = (aSizeBits + 5) / 6;
for (int aCharNum = 0; aCharNum < aNumChars; aCharNum++)
aString += gWebEncodeMap[ReadNumBits(6, false)];
mReadBitPos = anOldReadBitPos;
return aString;
}
std::wstring Buffer::UTF8ToWideString() const
{
const char* aData = (const char*)GetDataPtr();
int aLen = GetDataLen();
bool firstChar = true;
std::wstring aString;
aString.reserve(aLen); // worst case
while (aLen > 0)
{
wchar_t aChar;
int aConsumed = GetUTF8Char(&aData, aLen, &aChar);
if (aConsumed == 0) break;
aLen -= aConsumed;
if (firstChar)
{
firstChar = false;
if (aChar == 0xFEFF) continue;
}
aString += aChar;
}
return aString;
}
void Buffer::FromWebString(const std::string& theString)
{
Clear();
if (theString.size() < 4)
return;
int aSizeBits = 0;
for (int aDigitNum = 0; aDigitNum < 8; aDigitNum++)
{
char aChar = theString[aDigitNum];
int aVal = 0;
if ((aChar >= '0') && (aChar <= '9'))
aVal = aChar - '0';
else if ((aChar >= 'A') && (aChar <= 'F'))
aVal = (aChar - 'A') + 10;
else if ((aChar >= 'a') && (aChar <= 'f'))
aVal = (aChar - 'f') + 10;
aSizeBits += (aVal << ((7 - aDigitNum) * 4));
}
int aCharIdx = 8;
int aNumBitsLeft = aSizeBits;
while (aNumBitsLeft > 0)
{
uchar aChar = theString[aCharIdx++];
int aVal = gWebDecodeMap[aChar];
int aNumBits = min(aNumBitsLeft, 6);
WriteNumBits(aVal, aNumBits);
aNumBitsLeft -= aNumBits;
}
SeekFront();
}
void Buffer::SeekFront() const
{
mReadBitPos = 0;
}
void Buffer::Clear()
{
mReadBitPos = 0;
mWriteBitPos = 0;
mDataBitSize = 0;
mData.clear();
}
void Buffer::WriteByte(uchar theByte)
{
if (mWriteBitPos % 8 == 0)
mData.push_back((char) theByte);
else
{
int anOfs = mWriteBitPos % 8;
mData[mWriteBitPos /8] |= theByte << anOfs;
mData.push_back((char) (theByte >> (8-anOfs)));
}
mWriteBitPos += 8;
if (mWriteBitPos > mDataBitSize)
mDataBitSize = mWriteBitPos;
}
void Buffer::WriteNumBits(int theNum, int theBits)
{
for (int aBitNum = 0; aBitNum < theBits; aBitNum++)
{
if (mWriteBitPos % 8 == 0)
mData.push_back(0);
if ((theNum & (1<<aBitNum)) != 0)
mData[mWriteBitPos/8] |= 1 << (mWriteBitPos % 8);
mWriteBitPos++;
}
if (mWriteBitPos > mDataBitSize)
mDataBitSize = mWriteBitPos;
}
int Buffer::GetBitsRequired(int theNum, bool isSigned)
{
if (theNum < 0) // two's compliment stuff
theNum = -theNum - 1;
int aNumBits = 0;
while (theNum >= 1<<aNumBits)
aNumBits++;
if (isSigned)
aNumBits++;
return aNumBits;
}
void Buffer::WriteBoolean(bool theBool)
{
WriteByte(theBool ? 1 : 0);
}
void Buffer::WriteShort(short theShort)
{
WriteByte((uchar)theShort);
WriteByte((uchar)(theShort >> 8));
}
void Buffer::WriteLong(long theLong)
{
WriteByte((uchar)theLong);
WriteByte((uchar)(theLong >> 8));
WriteByte((uchar)(theLong >> 16));
WriteByte((uchar)(theLong >> 24));
}
void Buffer::WriteString(const std::string& theString)
{
WriteShort((short) theString.length());
for (int i = 0; i < (int)theString.length(); i++)
WriteByte(theString[i]);
}
void Buffer::WriteUTF8String(const std::wstring& theString)
{
if ((mWriteBitPos & 7) != 0) // boo! let's get byte aligned.
mWriteBitPos = (mWriteBitPos + 8) & ~7;
WriteShort((short) theString.length());
for (int i = 0; i < (int)theString.length(); ++i)
{
const unsigned int c = (unsigned int)theString[i]; // just in case wchar_t is only 16 bits, and it generally is in visual studio
if (c < 0x80)
{
WriteByte((uchar)c);
}
else if (c < 0x800)
{
WriteByte((uchar)(0xC0 | (c>>6)));
WriteByte((uchar)(0x80 | (c & 0x3F)));
}
else if (c < 0x10000)
{
WriteByte((uchar)(0xE0 | c>>12));
WriteByte((uchar)(0x80 | ((c>>6) & 0x3F)));
WriteByte((uchar)(0x80 | (c & 0x3F)));
}
else if (c < 0x110000)
{
WriteByte((uchar)(0xF0 | (c>>18)));
WriteByte((uchar)(0x80 | ((c>>12) & 0x3F)));
WriteByte((uchar)(0x80 | ((c>>6) & 0x3F)));
WriteByte((uchar)(0x80 | (c & 0x3F)));
} // are the remaining ranges really necessary? add if so!
}
}
void Buffer::WriteLine(const std::string& theString)
{
WriteBytes((const uchar*) (theString + "\r\n").c_str(), (int) theString.length() + 2);
}
void Buffer::WriteBuffer(const ByteVector& theBuffer)
{
WriteLong((short) theBuffer.size());
for (int i = 0; i < (int)theBuffer.size(); i++)
WriteByte(theBuffer[i]);
}
void Buffer::WriteBytes(const uchar* theByte, int theCount)
{
for (int i = 0; i < theCount; i++)
WriteByte(theByte[i]);
}
void Buffer::SetData(const ByteVector& theBuffer)
{
mData = theBuffer;
mDataBitSize = mData.size() * 8;
}
void Buffer::SetData(uchar* thePtr, int theCount)
{
mData.clear();
mData.insert(mData.begin(), thePtr, thePtr + theCount);
mDataBitSize = mData.size() * 8;
}
uchar Buffer::ReadByte() const
{
if ((mReadBitPos + 7)/8 >= (int)mData.size())
{
return 0; // Underflow
}
if (mReadBitPos % 8 == 0)
{
uchar b = mData[mReadBitPos/8];
mReadBitPos += 8;
return b;
}
else
{
int anOfs = mReadBitPos % 8;
uchar b = 0;
b = mData[mReadBitPos/8] >> anOfs;
b |= mData[(mReadBitPos/8)+1] << (8 - anOfs);
mReadBitPos += 8;
return b;
}
}
int Buffer::ReadNumBits(int theBits, bool isSigned) const
{
int aByteLength = (int) mData.size();
int theNum = 0;
bool bset = false;
for (int aBitNum = 0; aBitNum < theBits; aBitNum++)
{
int aBytePos = mReadBitPos/8;
if (aBytePos >= aByteLength)
break;
if (bset = (mData[aBytePos] & (1<<(mReadBitPos%8))) != 0)
theNum |= 1<<aBitNum;
mReadBitPos++;
}
if ((isSigned) && (bset)) // sign extend
for (int aBitNum = theBits; aBitNum < 32; aBitNum++)
theNum |= 1<<aBitNum;
return theNum;
}
bool Buffer::ReadBoolean() const
{
return ReadByte() != 0;
}
short Buffer::ReadShort() const
{
short aShort = ReadByte();
aShort |= ((short) ReadByte() << 8);
return aShort;
}
long Buffer::ReadLong() const
{
long aLong = ReadByte();
aLong |= ((long) ReadByte()) << 8;
aLong |= ((long) ReadByte()) << 16;
aLong |= ((long) ReadByte()) << 24;
return aLong;
}
std::string Buffer::ReadString() const
{
std::string aString;
int aLen = ReadShort();
for (int i = 0; i < aLen; i++)
aString += (char) ReadByte();
return aString;
}
std::wstring Buffer::ReadUTF8String() const
{
if ((mReadBitPos & 7) != 0)
mReadBitPos = (mReadBitPos + 8) & ~7; // byte align the read position
std::wstring aString;
int aLen = ReadShort();
const char* aData = (const char*)(&mData[mReadBitPos/8]);
int aDataSizeBytes = (mDataBitSize - mReadBitPos)/8;
int i;
for (i = 0; aDataSizeBytes > 0 && i < aLen; ++i)
{
wchar_t aChar;
int aConsumed = GetUTF8Char(&aData, aDataSizeBytes, &aChar);
if (aConsumed == 0) break;
aDataSizeBytes -= aConsumed;
aString += aChar;
}
DBG_ASSERT(i == aLen); // if this fires, the UTF-8 data was malformed.
return aString;
}
std::string Buffer::ReadLine() const
{
std::string aString;
for (;;)
{
char c = ReadByte();
if ((c == 0) || (c == '\n'))
break;
if (c != '\r')
aString += c;
}
return aString;
}
void Buffer::ReadBytes(uchar* theData, int theLen) const
{
for (int i = 0; i < theLen; i++)
theData[i] = ReadByte();
}
void Buffer::ReadBuffer(ByteVector* theByteVector) const
{
theByteVector->clear();
ulong aLength = ReadLong();
theByteVector->resize(aLength);
ReadBytes(&(*theByteVector)[0], aLength);
}
const uchar* Buffer::GetDataPtr() const
{
if (mData.size() == 0)
return NULL;
return &mData[0];
}
int Buffer::GetDataLen() const
{
return (mDataBitSize + 7) / 8; // Round up
}
int Buffer::GetDataLenBits() const
{
return mDataBitSize;
}
ulong Buffer::GetCRC32(ulong theSeed) const
{
ulong aCRC = theSeed;
aCRC = UpdateCRC(aCRC, (const char*) &mData[0], (int) mData.size());
return aCRC;
}
bool Buffer::AtEnd() const
{
//return mReadBitPos >= (int)mData.size()*8;
return mReadBitPos >= mDataBitSize;
}
bool Buffer::PastEnd() const
{
return mReadBitPos > mDataBitSize;
}