1 /* $Id: zstream-bz.cc,v 1.2 2005/04/04 21:58:17 atterer Exp $ -*- C++ -*-
3 |_) /| Copyright (C) 2004-2005 | richard@
4 | \/¯| Richard Atterer | atterer.net
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License, version 2. See
8 the file COPYING for details.
10 Zlib compression layer which integrates with C++ streams
24 #include <serialize.hh>
26 #include <zstream-bz.hh>
27 //______________________________________________________________________
29 DEBUG_UNIT("zstream-bz")
33 // Turn zlib error codes/messages into C++ exceptions
34 void throwZerrorBz(int status) {
35 Assert(status != BZ_OK);
42 case -1: case -2: case -4: case -5:
43 case -6: case -7: case -8: case -9:
44 throw Zerror(status, ZibstreamBz::bzerrorstrings[-status]);
46 string m = subst("libbz error %1", status);
47 throw Zerror(status, m);
52 //______________________________________________________________________
54 const char* ZibstreamBz::bzerrorstrings[] = {
69 //______________________________________________________________________
71 /* libbz2 subdivides input data into chunks of 100000 to 900000 bytes,
72 depending on compression level. For best compression results, BZIP input
73 data chunks should be exactly as large as the block size. The bzip2
74 sources use n*100000-19 as the actual block size in one place, we'll use
75 n*100000-50 just to be safe. NB: chunkLim() is the OUTPUT size limit for
76 gzip, but the INPUT size limit for bzip2. */
77 void ZobstreamBz::open(bostream& s, int level, unsigned todoBufSz) {
79 // Unlike zlib, libbz2 does not support level==0
80 if (level == 0) level = 1;
81 compressLevel = level;
84 z.next_out = reinterpret_cast<char*>(zipBuf->data);
85 z.avail_out = (zipBuf == 0 ? 0 : ZIPDATA_SIZE);
86 z.total_in_lo32 = z.total_in_hi32 = 0;
87 debug("BZ2_bzCompressInit");
90 if (debug.enabled()) verbosity = 2;
92 int status = BZ2_bzCompressInit(&z, level, verbosity,
93 0/*default workFactor*/);
97 throwZerrorBz(status);
99 // Declare stream as open
101 Zobstream::open(s, 100000 * level - 50, todoBufSz);
102 debug("opened, chunkLim=%1", chunkLim());
104 //______________________________________________________________________
106 unsigned ZobstreamBz::partId() {
107 return 0x41544144u; // "DATA"
110 void ZobstreamBz::deflateEnd() {
111 int status = BZ2_bzCompressEnd(&z);
113 if (status != BZ_OK) throwZerrorBz(status);
116 void ZobstreamBz::deflateReset() {
117 int status = BZ2_bzCompressEnd(&z);
119 if (status != BZ_OK) throwZerrorBz(status);
122 z.next_out = reinterpret_cast<char*>(zipBuf->data);
123 z.avail_out = (zipBuf == 0 ? 0 : ZIPDATA_SIZE);
124 z.total_in_lo32 = z.total_in_hi32 = 0;
125 debug("BZ2_bzCompressInit deflateReset");
128 if (debug.enabled()) verbosity = 2;
130 status = BZ2_bzCompressInit(&z, compressLevel, verbosity,
131 0/*default workFactor*/);
135 throwZerrorBz(status);
137 //______________________________________________________________________
139 void ZobstreamBz::zip2(byte* start, unsigned len, bool finish) {
140 debug("zip2 %1 bytes at %2", len, start);
141 int flush = (finish ? BZ_FINISH : BZ_RUN);
144 if (z.total_in_lo32 <= chunkLim() && chunkLim() <= z.total_in_lo32 + len)
147 // true <=> must call BZ2_bzCompress() at least once
148 bool callLibBzOnce = (flush != BZ_RUN);
150 z.next_in = reinterpret_cast<char*>(start);
152 while (z.avail_in != 0 || z.avail_out == 0 || callLibBzOnce) {
153 callLibBzOnce = false;
155 // If big enough, finish and write out this chunk
156 int availInDifference = 0;
157 if (z.total_in_lo32 <= chunkLim()
158 && chunkLim() <= z.total_in_lo32 + z.avail_in) {
159 // Adjust avail_in to hit the bzip2 block size exactly
160 availInDifference = chunkLim() - z.total_in_lo32 - z.avail_in; // <0
164 if (z.avail_out == 0) {
165 // Get another output buffer object
167 if (zipBufLast == 0 || zipBufLast->next == 0) {
169 if (zipBuf == 0) zipBuf = zd;
170 if (zipBufLast != 0) zipBufLast->next = zd;
172 zd = zipBufLast->next;
175 z.next_out = reinterpret_cast<char*>(zd->data);
176 z.avail_out = ZIPDATA_SIZE;
177 //cerr << "Zob: new ZipData @ " << &zd->data << endl;
180 debug("deflate ni=%1 ai=%2 no=%3 ao=%4",
181 (void*)(z.next_in), z.avail_in, (void*)(z.next_out), z.avail_out);
182 //memset(z.next_in, 0, z.avail_in);
183 //memset(z.next_out, 0, z.avail_out);
184 z.avail_in += availInDifference;
185 debug("deflate ai=%1 availInDifference=%2 ti=%3",
186 z.avail_in, availInDifference, z.total_in_lo32);
187 int status = BZ2_bzCompress(&z, flush); // Call libbz2
188 z.avail_in -= availInDifference;
189 debug("deflated ni=%1 ai=%2 no=%3 ao=%4 status=%5", (void*)(z.next_in),
190 z.avail_in, (void*)(z.next_out), z.avail_out, status);
191 if (status == BZ_STREAM_END) {
192 unsigned ai = z.avail_in;
193 char* ni = z.next_in;
194 writeZipped(0x50495a42u); // BZIP
200 if (status == BZ_STREAM_END) continue;
201 if (status != BZ_RUN_OK && status != BZ_FINISH_OK) throwZerrorBz(status);