plugin/bz2: Streaming support

git-svn-id: svn://ultimatepp.org/upp/trunk@11739 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2018-01-28 09:44:32 +00:00
parent 0443a21f96
commit 4e945630e0
2 changed files with 272 additions and 18 deletions

View file

@ -1,7 +1,91 @@
#ifndef __Plugin_BZ2__
#define __Plugin_BZ2__
#ifdef flagWIN32
#include "lib/bzlib.h"
#else
#include <bzlib.h>
#endif
namespace Upp {
namespace bz2 {
class Lib {
enum { NONE, DEFLATE, INFLATE };
bz_stream z;
Buffer<char> 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<const void *, int> 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<int, int> progress = Null);
void BZ2Decompress(Stream& out, Stream& in, Gate<int, int> progress = Null);

View file

@ -1,22 +1,192 @@
#include <Core/Core.h>
#include <plugin/bz2/bz2.h>
#ifdef flagWIN32
#include "lib/bzlib.h"
#else
#include <bzlib.h>
#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<const char *>(ptr);
while(size) {
int psz = (int) min(size, INT_MAX / 4);
if(mode == DEFLATE)
total += size;
z.next_in = const_cast<char *>(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<int, int> progress)
@ -28,8 +198,8 @@ void BZ2Decompress(Stream& out, Stream& in, Gate<int, int> 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<int, int> progress)
enum { BUF_SIZE = 65536 };
Buffer<char> 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)
{