diff --git a/uppsrc/plugin/bz2/bz2.h b/uppsrc/plugin/bz2/bz2.h index 1322b1f48..71ded98e1 100644 --- a/uppsrc/plugin/bz2/bz2.h +++ b/uppsrc/plugin/bz2/bz2.h @@ -1,7 +1,91 @@ #ifndef __Plugin_BZ2__ #define __Plugin_BZ2__ +#ifdef flagWIN32 + #include "lib/bzlib.h" +#else + #include +#endif + namespace Upp { + +namespace bz2 { + class Lib { + enum { NONE, DEFLATE, INFLATE }; + + bz_stream z; + Buffer output; + int chunk; + int mode; + int total; + int compression_level; + bool error; + bool rdall; + bool eos; + String out; + + void SetError(bool v) { error = v; } + void PutOut(const void *ptr, int size); + void Pump(bool finish); + void Begin(); + void Free(); + int GzipHeader(const char *ptr, int size); + void Init(); + + public: + Event WhenOut; + + void Compress(); + void Decompress(bool all = true); + void Put(const void *ptr, int size); + void Put(const String& s) { Put(~s, s.GetCount()); } + void End(); + void Clear(); + + const String& Get() const { return out; } + operator const String&() const { return out; } + const String& operator~() const { return out; } + void ClearOut() { out.Clear(); } + + bool IsError() const { return error; } + bool IsEOS() const { return eos; } + + Lib& ChunkSize(int n); + Lib& Level(int compression_lvl) { compression_level = compression_lvl; return *this; } + + Lib(); + ~Lib(); + }; + + class CompressStream : public OutFilterStream { + Lib z; + + public: + void Open(Stream& out) { z.Compress(); Set(out, z); } + + Lib& ChunkSize(int n) { return z.ChunkSize(n); } + Lib& Level(int compression_lvl) { return z.Level(compression_lvl); } + + CompressStream() {} + CompressStream(Stream& out) { Open(out); } + ~CompressStream() { Close(); } + }; + + class DecompressStream : public InFilterStream { + Lib z; + + public: + void Open(Stream& in) { z.Decompress(); Set(in, z); } + Lib& ChunkSize(int n) { return z.ChunkSize(n); } + + DecompressStream() {} + DecompressStream(Stream& out) { Open(out); } + ~DecompressStream() { Close(); } + }; +} + +typedef bz2::CompressStream BZ2CompressStream; +typedef bz2::DecompressStream BZ2DecompressStream; void BZ2Compress(Stream& out, Stream& in, Gate progress = Null); void BZ2Decompress(Stream& out, Stream& in, Gate progress = Null); diff --git a/uppsrc/plugin/bz2/bz2upp.cpp b/uppsrc/plugin/bz2/bz2upp.cpp index c3841a46a..ee7ece67d 100644 --- a/uppsrc/plugin/bz2/bz2upp.cpp +++ b/uppsrc/plugin/bz2/bz2upp.cpp @@ -1,22 +1,192 @@ #include #include -#ifdef flagWIN32 - #include "lib/bzlib.h" -#else - #include -#endif +#define LDUMP(x) // DDUMP(x) +#define LLOG(x) // DLOG(x) namespace Upp { - -static void* bzalloc_new(void *opaque, int items, int size) -{ - return new byte[items * size]; -} - -static void bzfree_new(void *opaque, void *addr) -{ - delete[] (byte *)addr; +namespace bz2 { + static void* bzalloc_new(void *opaque, int items, int size) + { + return new byte[items * size]; + } + + static void bzfree_new(void *opaque, void *addr) + { + delete[] (byte *)addr; + } + + + void Lib::Begin() + { + Free(); + SetError(false); + out.Clear(); + } + + void Lib::Compress() + { + Begin(); + if(BZ2_bzCompressInit(&z, compression_level, 0, 30) != BZ_OK) + Panic("BZ2_bzCompressInit failed"); + mode = DEFLATE; + } + + void Lib::Decompress(bool all) + { + Begin(); + rdall = all; + eos = false; + if(BZ2_bzDecompressInit(&z, 0, 0) != BZ_OK) + Panic("BZ2_bzDecompressInit failed"); + mode = INFLATE; + } + + void Lib::Pump(bool finish) + { + if(error) + return; + ASSERT(mode); + if(!output) + output.Alloc(chunk); + if (mode == INFLATE) { + z.avail_out = chunk; + z.next_out = output; + while (z.avail_in) { + const int code = BZ2_bzDecompress(&z); + const int count = chunk - z.avail_out; + if(count) { + WhenOut((const char *)~output, count); + if (error) { + Free(); + break; + } + total += count; + z.avail_out = chunk; + z.next_out = output; + } + if(code == BZ_STREAM_END) { + if (rdall) { + BZ2_bzDecompressEnd(&z); + if(BZ2_bzDecompressInit(&z, 0, 0) != BZ_OK) + Panic("BZ2_bzDecompressInit failed"); + } + else + eos = true; + } + if(code != BZ_OK && code != BZ_STREAM_END) { + LLOG("BZLIB ERROR " << code); + Free(); + SetError(true); + break; + } + } + } else { + const int flush = finish ? BZ_FINISH : BZ_RUN; + z.avail_out = chunk; + z.next_out = output; + while (z.avail_in) { + const int code = BZ2_bzCompress(&z, flush); + const int count = chunk - z.avail_out; + if(count) { + WhenOut((const char *)~output, count); + if (error) { + Free(); + break; + } + z.avail_out = chunk; + z.next_out = output; + } + if(code != BZ_RUN_OK && code != BZ_FINISH_OK) { + LLOG("BZLIB ERROR " << code); + Free(); + SetError(true); + break; + } + } + } + } + + void Lib::Put(const void *ptr, int size) + { + if(error) + return; + LLOG("BZLIB Put " << size); + const char *p = reinterpret_cast(ptr); + while(size) { + int psz = (int) min(size, INT_MAX / 4); + if(mode == DEFLATE) + total += size; + z.next_in = const_cast(p); + z.avail_in = size; + Pump(false); + size -= psz; + p += psz; + } + } + + void Lib::PutOut(const void *ptr, int size) + { + LLOG("BZLIB PutOut " << out.GetCount()); + out.Cat((const char *)ptr, (int)size); + } + + void Lib::End() + { + LLOG("BZLIB End"); + if(mode != INFLATE) + Pump(true); + Free(); + } + + void Lib::Free() + { + if(mode == INFLATE) + BZ2_bzDecompressEnd(&z); + else if(mode == DEFLATE) + BZ2_bzCompressEnd(&z); + mode = NONE; + total = 0; + } + + void Lib::Init() + { + mode = NONE; + SetError(false); + rdall = true; + eos = false; + Zero(z); + } + + void Lib::Clear() + { + Free(); + Init(); + } + + Lib& Lib::ChunkSize(int n) + { + ASSERT(n < INT_MAX / 4); + output.Clear(); + chunk = n; + return *this; + } + + Lib::Lib() + { + Init(); + z.bzalloc = bzalloc_new; + z.bzfree = bzfree_new; + z.opaque = 0; + chunk = 4096; + WhenOut = callback(this, &Lib::PutOut); + compression_level = 9; + } + + Lib::~Lib() + { + Free(); + } } void BZ2Decompress(Stream& out, Stream& in, Gate progress) @@ -28,8 +198,8 @@ void BZ2Decompress(Stream& out, Stream& in, Gate progress) return; bz_stream z; Zero(z); - z.bzalloc = bzalloc_new; - z.bzfree = bzfree_new; + z.bzalloc = bz2::bzalloc_new; + z.bzfree = bz2::bzfree_new; z.opaque = 0; if(BZ2_bzDecompressInit(&z, 0, 0) != BZ_OK) { @@ -80,8 +250,8 @@ void BZ2Compress(Stream& out, Stream& in, Gate progress) enum { BUF_SIZE = 65536 }; Buffer input(BUF_SIZE), output(BUF_SIZE); bz_stream z; - z.bzalloc = bzalloc_new; - z.bzfree = bzfree_new; + z.bzalloc = bz2::bzalloc_new; + z.bzfree = bz2::bzfree_new; z.opaque = 0; if(BZ2_bzCompressInit(&z, 9, 0, 30) != BZ_OK) {