123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- /* Copyright (c) 2002-2012 Croteam Ltd.
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as published by
- the Free Software Foundation
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
- #include "stdh.h"
- #include <Engine/Base/Stream.h>
- #include <Engine/Network/Compression.h>
- #include <Engine/Base/Synchronization.h>
- #include <Engine/zlib/zlib.h>
- extern CTCriticalSection zip_csLock; // critical section for access to zlib functions
- /* Unpack from stream to stream. */
- void CCompressor::UnpackStream_t(CTMemoryStream &strmSrc, CTStream &strmDst) // throw char *
- {
- // read the header
- SLONG slSizeDst, slSizeSrc;
- strmSrc>>slSizeDst;
- strmSrc>>slSizeSrc;
- // get the buffer of source stream
- UBYTE *pubSrc = strmSrc.mstrm_pubBuffer + strmSrc.mstrm_slLocation;
- // allocate buffer for decompression
- UBYTE *pubDst = (UBYTE*)AllocMemory(slSizeDst);
- // compress there
- BOOL bOk = Unpack(pubSrc, slSizeSrc, pubDst, slSizeDst);
- // if failed
- if (!bOk) {
- // report error
- FreeMemory(pubDst);
- ThrowF_t(TRANS("Error while unpacking a stream."));
- }
- // write the uncompressed data to destination
- strmDst.Write_t(pubDst, slSizeDst);
- strmDst.SetPos_t(0);
- FreeMemory(pubDst);
- }
- void CCompressor::PackStream_t(CTMemoryStream &strmSrc, CTStream &strmDst) // throw char *
- {
- // get the buffer of source stream
- UBYTE *pubSrc = strmSrc.mstrm_pubBuffer + strmSrc.mstrm_slLocation;
- SLONG slSizeSrc = strmSrc.GetStreamSize();
- // allocate buffer for compression
- SLONG slSizeDst = NeededDestinationSize(slSizeSrc);
- UBYTE *pubDst = (UBYTE*)AllocMemory(slSizeDst);
- // compress there
- BOOL bOk = Pack(pubSrc, slSizeSrc, pubDst, slSizeDst);
- // if failed
- if (!bOk) {
- // report error
- FreeMemory(pubDst);
- ThrowF_t(TRANS("Error while packing a stream."));
- }
- // write the header to destination
- strmDst<<slSizeSrc;
- strmDst<<slSizeDst;
- // write the compressed data to destination
- strmDst.Write_t(pubDst, slSizeDst);
- FreeMemory(pubDst);
- }
- /////////////////////////////////////////////////////////////////////
- // RLE compressor
- /*
- RLE packed format(s):
- NOTE:
- In order to decompress packed data, size of original data must be known, it is not
- written in the packed data by the algorithms. Depending on the usage, data size may be
- included in the header by the called function. This generally saves even more space
- if smaller chunks of known size are compressed (e.g. small networks packets).
- Basic implemented packing format here is BYTE-BYTE. It packes data by bytes and uses bytes
- for packing codes. Other sized RLE formats can be implemented as needed (WORD-WORD,
- LONG-LONG, BYTE-WORD, WORD-BYTE etc.).
- Packed data is divided in sections, each section has a leading byte as code and compressed
- data following it. The data is interpreted differently, depending on the code.
- 1) CODE<0 => REPLICATION
- If the code is negative, following data is one byte that is replicated given number of
- times:
- CODE DATA -> DATA DATA DATA ... DATA ((-CODE)+1 times)
- (note that it is not possible to encode just one byte this way, since one byte can be coded
- well with copying)
- (count = -code+1 => code = -count+1)
- 2) CODE>=0 => COPYING
- If the code is positive, following data is given number of bytes that are just copied:
- CODE DAT0 DAT1 ... DATn -> DAT0 DAT1 ... DATn (n=CODE)
- (note that CODE=0 means copy next one byte)
- (count = code+1 => code = count-1)
- Packing algorithm used here relies on the destination buffer being big enough to
- hold packed data. Generally, for BYTE-BYTE packing, maximum buffer is 129/128 of
- original size (degenerate case with no data replication).
- */
- /*
- * Calculate needed size for destination buffer when packing memory.
- */
- SLONG CRLEBBCompressor::NeededDestinationSize(SLONG slSourceSize)
- {
- // calculate worst case possible for size of RLEBB packed data
- // *129/128+1 would be enough, but we add some more to ensure that we don't
- // overwrite the temporary buffer
- return slSourceSize*129/128 + 5;
- }
- // on entry, slDstSize holds maximum size of output buffer,
- // on exit, it is filled with resulting size
- /* Pack a chunk of data using given compression. */
- BOOL CRLEBBCompressor::Pack(const void *pvSrc, SLONG slSrcSize, void *pvDst, SLONG &slDstSize)
- {
- // cannot pack zero bytes
- ASSERT(slSrcSize>=1);
- // calculate limits for source and destination buffers
- const SBYTE *pbSourceFirst = (const SBYTE *)pvSrc; // start marker
- const SBYTE *pbSourceLimit = (const SBYTE *)pvSrc+slSrcSize; // end marker
- // SLONG slDestinationSize=NeededDestinationSize(slSrcSize);
- SBYTE *pbDestinationFirst = (SBYTE *)pvDst; // start marker
- SBYTE *pbDestinationLimit = (SBYTE *)pvDst+slDstSize; // end marker
- UBYTE *pbCountFirst = (UBYTE *)pbDestinationLimit-slSrcSize; // start marker
- UBYTE *pbCountLimit = (UBYTE *)pbDestinationLimit; // end marker
- {
- /* PASS 1: Use destination buffer to cache number of forward-same bytes. */
- // set the count of the last byte to one
- UBYTE *pbCount = pbCountLimit-1;
- *pbCount-- = 1;
- // for all bytes from one before last to the first one
- for(const SBYTE *pbSource = pbSourceLimit-2;
- pbSource>=pbSourceFirst;
- pbSource--, pbCount--) {
- // if the byte is same as its successor, and the count will fit in code
- if (pbSource[0]==pbSource[1] && (SLONG)pbCount[1]+1<=-(SLONG)MIN_SBYTE) {
- // set its count to the count of its successor plus one
- pbCount[0] = pbCount[1]+1;
- // if the byte is different than its successor
- } else {
- // set its count to one
- pbCount[0] = 1;
- }
- }
- }
- /* PASS 2: Pack bytes from source to the destination buffer. */
- // start at the beginning of the buffers
- const SBYTE *pbSource = pbSourceFirst;
- const UBYTE *pbCount = pbCountFirst;
- SBYTE *pbDestination = pbDestinationFirst;
- // while there is some data to pack
- while(pbSource<pbSourceLimit) {
- ASSERT(pbCount<pbCountLimit);
- // if current byte is replicated
- if (*pbCount>1) {
- // write the replicate-packed data
- INDEX ctSameBytes = (INDEX)*pbCount;
- SLONG slCode = -ctSameBytes+1;
- ASSERT((SLONG)MIN_SBYTE<=slCode && slCode<0);
- *pbDestination++ = (SBYTE)slCode;
- *pbDestination++ = pbSource[0];
- pbSource+=ctSameBytes;
- pbCount +=ctSameBytes;
- // if current byte is not replicated
- } else {
- // count bytes to copy before encountering byte replicated more than 3 times
- INDEX ctDiffBytes=1;
- while( (ctDiffBytes < (SLONG)MAX_SBYTE + 1)
- && (&pbSource[ctDiffBytes]<pbSourceLimit) ) {
- if ((SLONG)pbCount[ctDiffBytes-1]<=3) {
- ctDiffBytes++;
- } else {
- break;
- }
- }
- // write the copy-packed data
- SLONG slCode = ctDiffBytes-1;
- ASSERT(0<=slCode && slCode<=(SLONG)MAX_SBYTE);
- *pbDestination++ = (SBYTE)slCode;
- memcpy(pbDestination, pbSource, ctDiffBytes);
- pbSource += ctDiffBytes;
- pbCount += ctDiffBytes;
- pbDestination += ctDiffBytes;
- }
- }
- // packing must exactly be finished now
- ASSERT(pbSource==pbSourceLimit);
- ASSERT(pbCount ==pbCountLimit);
- // calculate size of packed data
- slDstSize = pbDestination-pbDestinationFirst;
- return TRUE;
- }
- // on entry, slDstSize holds maximum size of output buffer,
- // on exit, it is filled with resulting size
- /* Unpack a chunk of data using given compression. */
- BOOL CRLEBBCompressor::Unpack(const void *pvSrc, SLONG slSrcSize, void *pvDst, SLONG &slDstSize)
- {
- const SBYTE *pbSource = (const SBYTE *)pvSrc; // current pointer
- const SBYTE *pbSourceLimit = (const SBYTE *)pvSrc+slSrcSize; // end marker
- SBYTE *pbDestination = (SBYTE *)pvDst; // current pointer
- SBYTE *pbDestinationFirst = (SBYTE *)pvDst; // start marker
- // repeat
- do {
- // get code
- SLONG slCode = *pbSource++;
- // if it is replication
- if (slCode<0) {
- // get next byte and replicate it given number of times
- INDEX ctSameBytes = -slCode+1;
- memset(pbDestination, *pbSource++, ctSameBytes);
- pbDestination += ctSameBytes;
- // if it is copying
- } else {
- // copy given number of next bytes
- INDEX ctCopyBytes = slCode+1;
- memcpy(pbDestination, pbSource, ctCopyBytes);
- pbSource += ctCopyBytes;
- pbDestination += ctCopyBytes;
- }
- // until all data is unpacked
- } while (pbSource<pbSourceLimit);
- // data must be unpacked correctly
- ASSERT(pbSource==pbSourceLimit);
- // calculate size of data that was unpacked
- slDstSize = pbDestination-pbDestinationFirst;
- return TRUE;
- }
- /////////////////////////////////////////////////////////////////////
- // LZRW1 compressor
- // uses algorithm by Ross Williams
- //#define TRUE 1
- //#define UBYTE unsigned char /* Unsigned byte (1 byte ) */
- //#define UWORD unsigned int /* Unsigned word (2 bytes) */
- //#define ULONG unsigned long /* Unsigned longword (4 bytes) */
- #define FLAG_BYTES 1 /* Number of bytes used by copy flag. */
- #define FLAG_COMPRESS 0 /* Signals that compression occurred. */
- #define FLAG_COPY 1 /* Signals that a copyover occurred. */
- //void fast_copy(p_src,p_dst,len) /* Fast copy routine. */
- //UBYTE *p_src,*p_dst; {while (len--) *p_dst++=*p_src++;}
- inline void fast_copy(const UBYTE *p_src, UBYTE *p_dst, SLONG len)
- {
- memcpy(p_dst, p_src, len);
- }
- /******************************************************************************/
- void lzrw1_compress(const UBYTE *p_src_first, ULONG src_len,UBYTE *p_dst_first, ULONG *p_dst_len)
- /* Input : Specify input block using p_src_first and src_len. */
- /* Input : Point p_dst_first to the start of the output zone (OZ). */
- /* Input : Point p_dst_len to a ULONG to receive the output length. */
- /* Input : Input block and output zone must not overlap. */
- /* Output : Length of output block written to *p_dst_len. */
- /* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
- /* Output : May write in OZ=Mem[p_dst_first..p_dst_first+src_len+256-1].*/
- /* Output : Upon completion guaranteed *p_dst_len<=src_len+FLAG_BYTES. */
- #define PS *p++!=*s++ /* Body of inner unrolled matching loop. */
- #define ITEMMAX 16 /* Maximum number of bytes in an expanded item. */
- {const UBYTE *p_src=p_src_first;
- UBYTE *p_dst=p_dst_first;
- const UBYTE *p_src_post=p_src_first+src_len;
- UBYTE *p_dst_post=p_dst_first+src_len;
- const UBYTE *p_src_max1=p_src_post-ITEMMAX,*p_src_max16=p_src_post-16*ITEMMAX;
- const UBYTE *hash[4096];
- UBYTE *p_control; UWORD control=0,control_bits=0;
- *p_dst=FLAG_COMPRESS; p_dst+=FLAG_BYTES; p_control=p_dst; p_dst+=2;
- while (TRUE)
- {const UBYTE *p,*s; UWORD unroll=16,len,index; ULONG offset;
- if (p_dst>p_dst_post) goto overrun;
- if (p_src>p_src_max16)
- {unroll=1;
- if (p_src>p_src_max1)
- {if (p_src==p_src_post) break; goto literal;}}
- begin_unrolled_loop:
- index=((40543*((((p_src[0]<<4)^p_src[1])<<4)^p_src[2]))>>4) & 0xFFF;
- p=hash[index];
- hash[index]=s=p_src;
- offset=s-p;
- if (offset>4095 || p<p_src_first || offset==0 || PS || PS || PS)
- {literal: *p_dst++=*p_src++; control>>=1; control_bits++;}
- else
- {PS || PS || PS || PS || PS || PS || PS ||
- PS || PS || PS || PS || PS || PS || s++; len=s-p_src-1;
- *p_dst++=(UBYTE)(((offset&0xF00)>>4)+(len-1)); *p_dst++=(UBYTE)(offset&0xFF);
- p_src+=len; control=(control>>1)|0x8000; control_bits++;}
- /*end_unrolled_loop:*/ if (--unroll) goto begin_unrolled_loop;
- if (control_bits==16)
- {*p_control=control&0xFF; *(p_control+1)=control>>8;
- p_control=p_dst; p_dst+=2; control=control_bits=0;}
- }
- control>>=16-control_bits;
- *p_control++=control&0xFF; *p_control++=control>>8;
- if (p_control==p_dst) p_dst-=2;
- *p_dst_len=(p_dst-p_dst_first);
- return;
- overrun: fast_copy(p_src_first,p_dst_first+FLAG_BYTES,src_len);
- *p_dst_first=FLAG_COPY; *p_dst_len=src_len+FLAG_BYTES;
- }
- /******************************************************************************/
- void lzrw1_decompress(const UBYTE *p_src_first, ULONG src_len, UBYTE *p_dst_first, ULONG *p_dst_len)
- /* Input : Specify input block using p_src_first and src_len. */
- /* Input : Point p_dst_first to the start of the output zone. */
- /* Input : Point p_dst_len to a ULONG to receive the output length. */
- /* Input : Input block and output zone must not overlap. User knows */
- /* Input : upperbound on output block length from earlier compression. */
- /* Input : In any case, maximum expansion possible is eight times. */
- /* Output : Length of output block written to *p_dst_len. */
- /* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
- /* Output : Writes only in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
- {UWORD controlbits=0, control;
- const UBYTE *p_src=p_src_first+FLAG_BYTES;
- UBYTE *p_dst=p_dst_first;
- const UBYTE *p_src_post=p_src_first+src_len;
- if (*p_src_first==FLAG_COPY)
- {fast_copy(p_src_first+FLAG_BYTES,p_dst_first,src_len-FLAG_BYTES);
- *p_dst_len=src_len-FLAG_BYTES; return;}
- while (p_src!=p_src_post)
- {if (controlbits==0)
- {control=*p_src++; control|=(*p_src++)<<8; controlbits=16;}
- if (control&1)
- {UWORD offset,len; UBYTE *p;
- offset=(*p_src&0xF0)<<4; len=1+(*p_src++&0xF);
- offset+=*p_src++&0xFF; p=p_dst-offset;
- while (len--) *p_dst++=*p++;}
- else
- *p_dst++=*p_src++;
- control>>=1; controlbits--;
- }
- *p_dst_len=p_dst-p_dst_first;
- }
- /*
- * Calculate needed size for destination buffer when packing memory.
- */
- SLONG CLZCompressor::NeededDestinationSize(SLONG slSourceSize)
- {
- // calculate worst case possible for size of LZ packed data
- return slSourceSize+256;
- }
- // on entry, slDstSize holds maximum size of output buffer,
- // on exit, it is filled with resulting size
- /* Pack a chunk of data using given compression. */
- BOOL CLZCompressor::Pack(const void *pvSrc, SLONG slSrcSize, void *pvDst, SLONG &slDstSize)
- {
- // this is just wrapper for original function by Ross Williams
- SLONG slDestinationSizeResult = slDstSize;
- lzrw1_compress(
- (const UBYTE *)pvSrc, (ULONG)slSrcSize,
- (UBYTE *)pvDst, (ULONG *)&slDestinationSizeResult);
- slDstSize = slDestinationSizeResult;
- return TRUE;
- }
- // on entry, slDstSize holds maximum size of output buffer,
- // on exit, it is filled with resulting size
- /* Unpack a chunk of data using given compression. */
- BOOL CLZCompressor::Unpack(const void *pvSrc, SLONG slSrcSize, void *pvDst, SLONG &slDstSize)
- {
- // this is just wrapper for original function by Ross Williams
- SLONG slDestinationSizeResult = slDstSize;
- lzrw1_decompress(
- (const UBYTE *)pvSrc, (ULONG)slSrcSize,
- (UBYTE *)pvDst, (ULONG *)&slDestinationSizeResult);
- slDstSize = slDestinationSizeResult;
- return TRUE;
- }
- /* Calculate needed size for destination buffer when packing memory. */
- SLONG CzlibCompressor::NeededDestinationSize(SLONG slSourceSize)
- {
- // calculate worst case possible for size of zlib packed data
- // NOTE: zlib docs state 0.1% of uncompressed size + 12 bytes,
- // we just want to be on the safe side
- return SLONG(slSourceSize*1.1f)+32;
- }
- // on entry, slDstSize holds maximum size of output buffer,
- // on exit, it is filled with resulting size
- /* Pack a chunk of data using given compression. */
- BOOL CzlibCompressor::Pack(const void *pvSrc, SLONG slSrcSize, void *pvDst, SLONG &slDstSize)
- {
- /*
- int ZEXPORT compress (dest, destLen, source, sourceLen)
- Bytef *dest;
- uLongf *destLen;
- const Bytef *source;
- uLong sourceLen;
- */
- CTSingleLock slZip(&zip_csLock, TRUE);
- int iResult = compress(
- (UBYTE *)pvDst, (ULONG *)&slDstSize,
- (const UBYTE *)pvSrc, (ULONG)slSrcSize);
- if (iResult==Z_OK) {
- return TRUE;
- } else {
- return FALSE;
- }
- }
- // on entry, slDstSize holds maximum size of output buffer,
- // on exit, it is filled with resulting size
- /* Unpack a chunk of data using given compression. */
- BOOL CzlibCompressor::Unpack(const void *pvSrc, SLONG slSrcSize, void *pvDst, SLONG &slDstSize)
- {
- /*
- int ZEXPORT uncompress (dest, destLen, source, sourceLen)
- Bytef *dest;
- uLongf *destLen;
- const Bytef *source;
- uLong sourceLen;
- */
- CTSingleLock slZip(&zip_csLock, TRUE);
- int iResult = uncompress(
- (UBYTE *)pvDst, (ULONG *)&slDstSize,
- (const UBYTE *)pvSrc, (ULONG)slSrcSize);
- if (iResult==Z_OK) {
- return TRUE;
- } else {
- return FALSE;
- }
- }
|