void DataCompressor::Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output ) { // Don't use this for small files as you will just make them bigger! assert(sizeInBytes > 2048); unsigned int frequencyTable[ 256 ]; unsigned int i; memset(frequencyTable,0,256*sizeof(unsigned int)); for (i=0; i < sizeInBytes; i++) ++frequencyTable[userData[i]]; HuffmanEncodingTree tree; unsigned int writeOffset1, writeOffset2, bitsUsed1, bitsUsed2; tree.GenerateFromFrequencyTable(frequencyTable); output->WriteCompressed(sizeInBytes); for (i=0; i < 256; i++) output->WriteCompressed(frequencyTable[i]); output->AlignWriteToByteBoundary(); writeOffset1=output->GetWriteOffset(); output->Write((unsigned int)0); // Dummy value bitsUsed1=output->GetNumberOfBitsUsed(); tree.EncodeArray(userData, sizeInBytes, output); bitsUsed2=output->GetNumberOfBitsUsed(); writeOffset2=output->GetWriteOffset(); output->SetWriteOffset(writeOffset1); output->Write(bitsUsed2-bitsUsed1); // Go back and write how many bits were used for the encoding output->SetWriteOffset(writeOffset2); }
bool StringCompressor::DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, int languageID ) { HuffmanEncodingTree *huffmanEncodingTree; if (huffmanEncodingTrees.Has(languageID)==false) return false; huffmanEncodingTree=huffmanEncodingTrees.Get(languageID); unsigned short stringBitLength; int bytesInStream; output[ 0 ] = 0; if ( input->ReadCompressed( stringBitLength ) == false ) return false; if ( input->GetNumberOfUnreadBits() < stringBitLength ) return false; bytesInStream = huffmanEncodingTree->DecodeArray( input, stringBitLength, maxCharsToWrite, ( unsigned char* ) output ); if ( bytesInStream < maxCharsToWrite ) output[ bytesInStream ] = 0; else output[ maxCharsToWrite - 1 ] = 0; return true; }
unsigned DataCompressor::DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output ) { HuffmanEncodingTree tree; unsigned int bitsUsed, destinationSizeInBytes, decompressedBytes; unsigned int frequencyTable[ 256 ]; unsigned i; input->ReadCompressed(destinationSizeInBytes); for (i=0; i < 256; i++) input->ReadCompressed(frequencyTable[i]); input->AlignReadToByteBoundary(); if (input->Read(bitsUsed)==false) { // Read error #ifdef _DEBUG assert(0); #endif return 0; } *output = new unsigned char [destinationSizeInBytes]; tree.GenerateFromFrequencyTable(frequencyTable); decompressedBytes=tree.DecodeArray(input, bitsUsed, destinationSizeInBytes, *output ); assert(decompressedBytes==destinationSizeInBytes); return destinationSizeInBytes; }
void StringCompressor::EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, int languageID ) { HuffmanEncodingTree *huffmanEncodingTree; if (huffmanEncodingTrees.Has(languageID)==false) return; huffmanEncodingTree=huffmanEncodingTrees.Get(languageID); if ( input == 0 ) { output->WriteCompressed( (unsigned short) 0 ); return; } RakNet::BitStream encodedBitStream; unsigned short stringBitLength; int charsToWrite; if ( maxCharsToWrite<=0 || ( int ) strlen( input ) < maxCharsToWrite ) charsToWrite = ( int ) strlen( input ); else charsToWrite = maxCharsToWrite - 1; huffmanEncodingTree->EncodeArray( ( unsigned char* ) input, charsToWrite, &encodedBitStream ); stringBitLength = ( unsigned short ) encodedBitStream.GetNumberOfBitsUsed(); output->WriteCompressed( stringBitLength ); output->WriteBits( encodedBitStream.GetData(), stringBitLength ); }
void StringCompressor::GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, int languageID ) { HuffmanEncodingTree *huffmanEncodingTree; if (huffmanEncodingTrees.Has(languageID)) { huffmanEncodingTree = huffmanEncodingTrees.Get(languageID); delete huffmanEncodingTree; } unsigned index; unsigned int frequencyTable[ 256 ]; if ( inputLength == 0 ) return ; // Zero out the frequency table memset( frequencyTable, 0, sizeof( frequencyTable ) ); // Generate the frequency table from the strings for ( index = 0; index < inputLength; index++ ) frequencyTable[ input[ index ] ] ++; // Build the tree huffmanEncodingTree = new HuffmanEncodingTree; huffmanEncodingTree->GenerateFromFrequencyTable( frequencyTable ); huffmanEncodingTrees.Set(languageID, huffmanEncodingTree); }
StringCompressor::StringCompressor() { DataStructures::Map<int, HuffmanEncodingTree *>::IMPLEMENT_DEFAULT_COMPARISON(); // Make a default tree immediately, since this is used for RPC possibly from multiple threads at the same time HuffmanEncodingTree *huffmanEncodingTree = new HuffmanEncodingTree; huffmanEncodingTree->GenerateFromFrequencyTable( englishCharacterFrequencies ); huffmanEncodingTrees.Set(0, huffmanEncodingTree); }
// Generate a HuffmanEncodingTree. // You can also use GetFrequencyTable and GenerateFromFrequencyTable in the tree itself HuffmanEncodingTree * HuffmanEncodingTreeFactory::GenerateTree( void ) { HuffmanEncodingTree * huffmanEncodingTree = new HuffmanEncodingTree; huffmanEncodingTree->GenerateFromFrequencyTable( frequency ); return huffmanEncodingTree; }
void FileListTransfer::Send(FileList *fileList, RakPeerInterface *rakPeer, PlayerID recipient, unsigned short setID, PacketPriority priority, char orderingChannel, bool compressData) { RakNet::BitStream outBitstream, encodedData; HuffmanEncodingTree tree; unsigned int frequencyTable[ 256 ]; unsigned int i,j; unsigned totalCompressedLength, totalLength; DataStructures::Queue<FileListNode> compressedFiles; FileListNode n; if (compressData) { memset(frequencyTable,0,256*sizeof(unsigned int)); for (i=0; i < fileList->fileList.Size(); i++) { for (j=0; j < fileList->fileList[i].dataLength; j++) { ++frequencyTable[(unsigned char)(fileList->fileList[i].data[j])]; } } tree.GenerateFromFrequencyTable(frequencyTable); // Compress all the files, so we know the total compressed size to be sent totalCompressedLength=totalLength=0; for (i=0; i < fileList->fileList.Size(); i++) { encodedData.Reset(); // Why send compressed chunks if we are not sending the whole file? assert(fileList->fileList[i].fileLength==fileList->fileList[i].fileLength); tree.EncodeArray((unsigned char*)fileList->fileList[i].data, fileList->fileList[i].dataLength, &encodedData); n.dataLength=encodedData.GetNumberOfBitsUsed(); totalCompressedLength+=BITS_TO_BYTES(n.dataLength); totalLength+=fileList->fileList[i].fileLength; n.data = new char[BITS_TO_BYTES(n.dataLength)]; memcpy(n.data, encodedData.GetData(), BITS_TO_BYTES(n.dataLength)); compressedFiles.Push(n); } } // Write the chunk header, which contains the frequency table, the total number of files, and the total number of bytes bool anythingToWrite; outBitstream.Write((unsigned char)ID_FILE_LIST_TRANSFER_HEADER); outBitstream.Write(setID); anythingToWrite=fileList->fileList.Size()>0; outBitstream.Write(anythingToWrite); if (anythingToWrite) { if (compressData) { outBitstream.Write(true); for (i=0; i < 256; i++) outBitstream.WriteCompressed(frequencyTable[i]); outBitstream.WriteCompressed(fileList->fileList.Size()); outBitstream.WriteCompressed(totalLength); outBitstream.WriteCompressed(totalCompressedLength); } else { outBitstream.Write(false); outBitstream.WriteCompressed(fileList->fileList.Size()); outBitstream.WriteCompressed(totalLength); } rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); // Send each possibly compressed file for (i=0; i < compressedFiles.Size(); i++) { outBitstream.Reset(); outBitstream.Write((unsigned char)ID_FILE_LIST_TRANSFER_FILE); outBitstream.Write(fileList->fileList[i].context); outBitstream.Write(setID); outBitstream.WriteCompressed(i); outBitstream.WriteCompressed(fileList->fileList[i].dataLength); // Original length if (compressData) outBitstream.WriteCompressed(compressedFiles[i].dataLength); // Compressed bitlength } stringCompressor->EncodeString(fileList->fileList[i].filename, 512, &outBitstream); if (compressData) { outBitstream.WriteBits((const unsigned char*)compressedFiles[i].data, compressedFiles[i].dataLength); delete [] compressedFiles[i].data; } else outBitstream.WriteBits((const unsigned char*)fileList->fileList[i].data, fileList->fileList[i].dataLength); rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); } } else rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); }