// See http://www.bzip.org/1.0.3/bzip2-manual-1.0.3.html#low-level for docs on bzip #include "MemoryCompressor.h" #include CompressorBase::CompressorBase() { output=0; allocatedSize=0; streamInited=false; stream.bzalloc=0; stream.bzfree=0; stream.opaque=0; totalRead=totalWritten=0; } CompressorBase::~CompressorBase() { } MemoryCompressor::~MemoryCompressor() { Clear(); } MemoryDecompressor::~MemoryDecompressor() { Clear(); } bool MemoryCompressor::Compress(char *input, const unsigned inputLength, bool finish) { int res; unsigned inBefore,outBefore, read; unsigned written; if (output==0) { allocatedSize=inputLength; if (allocatedSize < 1024) allocatedSize=1024; output=(char*)malloc(allocatedSize); } if (streamInited==false) { res = BZ2_bzCompressInit ( &stream, 9, // x 100K Block size. Memory to use = 400k + ( 8 x block size ). So this is 400K + 9 x 900K = 7.6 megabytes. Larger sizes give better compression. 0, // Verbosity. 0 ); // Default work factor streamInited=true; if (res!=BZ_OK) return false; } read=written=0; unsigned readThisSession; readThisSession=0; if (totalWritten==allocatedSize) { allocatedSize+=inputLength; output=(char*)realloc(output, allocatedSize); } while(1) { stream.next_out=output+totalWritten; stream.avail_in=inputLength-readThisSession; stream.avail_out=allocatedSize-totalWritten; stream.next_in=input+readThisSession; inBefore=stream.total_in_lo32; outBefore=stream.total_out_lo32; //printf("%i\n", stream.avail_in); res = BZ2_bzCompress( &stream, finish ? BZ_FINISH : BZ_RUN ); read=stream.total_in_lo32-inBefore; written=stream.total_out_lo32-outBefore; totalRead+=read; totalWritten+=written; readThisSession+=read; if ((stream.avail_in==0 && stream.avail_out>0) || (read==0 && written==0)) { if (finish) { allocatedSize=GetTotalOutputSize(); output=(char*)realloc(output,allocatedSize); BZ2_bzCompressEnd( &stream ); streamInited=false; } return true; } if (totalWritten==allocatedSize || read==0) { allocatedSize+=inputLength; output=(char*)realloc(output, allocatedSize); } } } bool MemoryDecompressor::Decompress(char *input, const unsigned inputLength, bool ignoreStreamEnd) { unsigned inBefore,outBefore, read; unsigned written; int res; if (output==0) { allocatedSize=inputLength*2; if (allocatedSize < 1024) allocatedSize=1024; output=(char*)malloc(allocatedSize); } if (streamInited==false) { res = BZ2_bzDecompressInit( &stream, 0, // Verbosity. 0 ); // Disable small memory usage streamInited=true; if (res!=BZ_OK) return false; } unsigned readThisSession; readThisSession=0; read=written=0; if (totalWritten==allocatedSize) { allocatedSize+=inputLength*4; output=(char*)realloc(output, allocatedSize); } while(1) { stream.next_out=output+totalWritten; stream.avail_in=inputLength-readThisSession; stream.avail_out=allocatedSize-totalWritten; stream.next_in=input+readThisSession; inBefore=stream.total_in_lo32; outBefore=stream.total_out_lo32; res = BZ2_bzDecompress( &stream ); read=stream.total_in_lo32-inBefore; written=stream.total_out_lo32-outBefore; readThisSession+=read; totalRead+=read; totalWritten+=written; if (res==BZ_STREAM_END) { BZ2_bzDecompressEnd( &stream ); if (ignoreStreamEnd==true) { // Stream end marker but there is more data so just keep reading res = BZ2_bzDecompressInit( &stream, 0, // Verbosity. 0 ); // Disable small memory usage } else { streamInited=false; return true; } } else if ((stream.avail_in==0 && stream.avail_out>0) || (read==0 && written==0)) { allocatedSize=GetTotalOutputSize(); output=(char*)realloc(output,allocatedSize); return true; } else if (res!=BZ_OK) { Clear(); return false; } if (totalWritten==allocatedSize || read==0) { allocatedSize+=inputLength*4; output=(char*)realloc(output, allocatedSize); } } } char *CompressorBase::GetOutput(void) const { return output; } unsigned CompressorBase::GetTotalOutputSize(void) const { return totalWritten; } unsigned CompressorBase::GetTotalInputSize(void) const { return totalRead; } void MemoryCompressor::Clear(void) { if (output) { free(output); output=0; } if (streamInited) BZ2_bzCompressEnd( &stream ); totalRead=totalWritten=0; } void MemoryDecompressor::Clear(void) { if (output) { free(output); output=0; } if (streamInited) BZ2_bzDecompressEnd( &stream ); }