KDECore
kgzipfilter.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kgzipfilter.h"
00021
00022 #include <time.h>
00023 #include <zlib.h>
00024 #include <kdebug.h>
00025 #include <QtCore/QIODevice>
00026
00027
00028
00029 #define ASCII_FLAG 0x01
00030 #define HEAD_CRC 0x02
00031 #define EXTRA_FIELD 0x04
00032 #define ORIG_NAME 0x08
00033 #define COMMENT 0x10
00034 #define RESERVED 0xE0
00035
00036
00037
00038 class KGzipFilter::Private
00039 {
00040 public:
00041 Private()
00042 : headerWritten(false), footerWritten(false), compressed(false), mode(0), crc(0)
00043 {
00044 zStream.zalloc = (alloc_func)0;
00045 zStream.zfree = (free_func)0;
00046 zStream.opaque = (voidpf)0;
00047 }
00048
00049 z_stream zStream;
00050 bool headerWritten;
00051 bool footerWritten;
00052 bool compressed;
00053 int mode;
00054 ulong crc;
00055 };
00056
00057 KGzipFilter::KGzipFilter()
00058 : d(new Private)
00059 {
00060 }
00061
00062
00063 KGzipFilter::~KGzipFilter()
00064 {
00065 delete d;
00066 }
00067
00068 void KGzipFilter::init(int mode)
00069 {
00070 init(mode, filterFlags() == WithHeaders ? GZipHeader : RawDeflate);
00071 }
00072
00073 void KGzipFilter::init(int mode, Flag flag)
00074 {
00075 d->zStream.next_in = Z_NULL;
00076 d->zStream.avail_in = 0;
00077 if ( mode == QIODevice::ReadOnly )
00078 {
00079 const int windowBits = (flag == RawDeflate)
00080 ? -MAX_WBITS
00081 : (flag == GZipHeader) ?
00082 MAX_WBITS + 32
00083 : MAX_WBITS ;
00084 const int result = inflateInit2(&d->zStream, windowBits);
00085 if ( result != Z_OK )
00086 kDebug(7005) << "inflateInit2 returned " << result;
00087
00088 } else if ( mode == QIODevice::WriteOnly )
00089 {
00090 int result = deflateInit2(&d->zStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
00091 if ( result != Z_OK )
00092 kDebug(7005) << "deflateInit returned " << result;
00093 } else {
00094 kWarning(7005) << "KGzipFilter: Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
00095 }
00096 d->mode = mode;
00097 d->compressed = true;
00098 d->headerWritten = false;
00099 d->footerWritten = false;
00100 }
00101
00102 int KGzipFilter::mode() const
00103 {
00104 return d->mode;
00105 }
00106
00107 void KGzipFilter::terminate()
00108 {
00109 if ( d->mode == QIODevice::ReadOnly )
00110 {
00111 int result = inflateEnd(&d->zStream);
00112 if ( result != Z_OK )
00113 kDebug(7005) << "inflateEnd returned " << result;
00114 } else if ( d->mode == QIODevice::WriteOnly )
00115 {
00116 int result = deflateEnd(&d->zStream);
00117 if ( result != Z_OK )
00118 kDebug(7005) << "deflateEnd returned " << result;
00119 }
00120 }
00121
00122
00123 void KGzipFilter::reset()
00124 {
00125 if ( d->mode == QIODevice::ReadOnly )
00126 {
00127 int result = inflateReset(&d->zStream);
00128 if ( result != Z_OK )
00129 kDebug(7005) << "inflateReset returned " << result;
00130 } else if ( d->mode == QIODevice::WriteOnly ) {
00131 int result = deflateReset(&d->zStream);
00132 if ( result != Z_OK )
00133 kDebug(7005) << "deflateReset returned " << result;
00134 d->headerWritten = false;
00135 d->footerWritten = false;
00136 }
00137 }
00138
00139 bool KGzipFilter::readHeader()
00140 {
00141
00142
00143
00144 #ifdef DEBUG_GZIP
00145 kDebug(7005) << "avail=" << d->zStream.avail_in;
00146 #endif
00147
00148 d->compressed = false;
00149 Bytef *p = d->zStream.next_in;
00150 int i = d->zStream.avail_in;
00151 if ((i -= 10) < 0) return false;
00152 #ifdef DEBUG_GZIP
00153 kDebug(7005) << "first byte is " << QString::number(*p,16);
00154 #endif
00155 if (*p++ != 0x1f) return false;
00156 #ifdef DEBUG_GZIP
00157 kDebug(7005) << "second byte is " << QString::number(*p,16);
00158 #endif
00159 if (*p++ != 0x8b) return false;
00160
00161 #if 0
00162 int method = *p++;
00163 int flags = *p++;
00164 if ((method != Z_DEFLATED) || (flags & RESERVED) != 0) return false;
00165 p += 6;
00166 if ((flags & EXTRA_FIELD) != 0)
00167 {
00168 if ((i -= 2) < 0) return false;
00169 int len = *p++;
00170 len += (*p++) << 8;
00171 if ((i -= len) < 0) return false;
00172 p += len;
00173 }
00174 if ((flags & ORIG_NAME) != 0)
00175 {
00176 #ifdef DEBUG_GZIP
00177 kDebug(7005) << "ORIG_NAME=" << (char*)p;
00178 #endif
00179 while( (i > 0) && (*p))
00180 {
00181 i--; p++;
00182 }
00183 if (--i <= 0) return false;
00184 p++;
00185 }
00186 if ((flags & COMMENT) != 0)
00187 {
00188 while( (i > 0) && (*p))
00189 {
00190 i--; p++;
00191 }
00192 if (--i <= 0) return false;
00193 p++;
00194 }
00195 if ((flags & HEAD_CRC) != 0)
00196 {
00197 if ((i-=2) < 0) return false;
00198 p += 2;
00199 }
00200
00201 d->zStream.avail_in = i;
00202 d->zStream.next_in = p;
00203 #endif
00204
00205 d->compressed = true;
00206 #ifdef DEBUG_GZIP
00207 kDebug(7005) << "header OK";
00208 #endif
00209 return true;
00210 }
00211
00212
00213 #define put_short(w) \
00214 *p++ = (uchar) ((w) & 0xff); \
00215 *p++ = (uchar) ((ushort)(w) >> 8);
00216
00217
00218 #define put_long(n) \
00219 put_short((n) & 0xffff); \
00220 put_short(((ulong)(n)) >> 16);
00221
00222 bool KGzipFilter::writeHeader( const QByteArray & fileName )
00223 {
00224 Bytef *p = d->zStream.next_out;
00225 int i = d->zStream.avail_out;
00226 *p++ = 0x1f;
00227 *p++ = 0x8b;
00228 *p++ = Z_DEFLATED;
00229 *p++ = ORIG_NAME;
00230 put_long( time( 0L ) );
00231 *p++ = 0;
00232 *p++ = 3;
00233
00234 uint len = fileName.length();
00235 for ( uint j = 0 ; j < len ; ++j )
00236 *p++ = fileName[j];
00237 *p++ = 0;
00238 int headerSize = p - d->zStream.next_out;
00239 i -= headerSize;
00240 Q_ASSERT(i>0);
00241 d->crc = crc32(0L, Z_NULL, 0);
00242 d->zStream.next_out = p;
00243 d->zStream.avail_out = i;
00244 d->headerWritten = true;
00245 return true;
00246 }
00247
00248 void KGzipFilter::writeFooter()
00249 {
00250 Q_ASSERT( d->headerWritten );
00251 if (!d->headerWritten) kDebug() << kBacktrace();
00252 Q_ASSERT(!d->footerWritten);
00253 Bytef *p = d->zStream.next_out;
00254 int i = d->zStream.avail_out;
00255
00256 put_long( d->crc );
00257
00258 put_long( d->zStream.total_in );
00259 i -= p - d->zStream.next_out;
00260 d->zStream.next_out = p;
00261 d->zStream.avail_out = i;
00262 d->footerWritten = true;
00263 }
00264
00265 void KGzipFilter::setOutBuffer( char * data, uint maxlen )
00266 {
00267 d->zStream.avail_out = maxlen;
00268 d->zStream.next_out = (Bytef *) data;
00269 }
00270 void KGzipFilter::setInBuffer( const char * data, uint size )
00271 {
00272 #ifdef DEBUG_GZIP
00273 kDebug(7005) << "avail_in=" << size;
00274 #endif
00275 d->zStream.avail_in = size;
00276 d->zStream.next_in = (Bytef*) data;
00277 }
00278 int KGzipFilter::inBufferAvailable() const
00279 {
00280 return d->zStream.avail_in;
00281 }
00282 int KGzipFilter::outBufferAvailable() const
00283 {
00284 return d->zStream.avail_out;
00285 }
00286
00287 KGzipFilter::Result KGzipFilter::uncompress_noop()
00288 {
00289
00290
00291
00292 if ( d->zStream.avail_in > 0 )
00293 {
00294 int n = (d->zStream.avail_in < d->zStream.avail_out) ? d->zStream.avail_in : d->zStream.avail_out;
00295 memcpy( d->zStream.next_out, d->zStream.next_in, n );
00296 d->zStream.avail_out -= n;
00297 d->zStream.next_in += n;
00298 d->zStream.avail_in -= n;
00299 return KFilterBase::Ok;
00300 } else
00301 return KFilterBase::End;
00302 }
00303
00304 KGzipFilter::Result KGzipFilter::uncompress()
00305 {
00306 Q_ASSERT ( d->mode == QIODevice::ReadOnly );
00307 if ( d->compressed )
00308 {
00309 #ifdef DEBUG_GZIP
00310 kDebug(7005) << "Calling inflate with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
00311 kDebug(7005) << " next_in=" << d->zStream.next_in;
00312 #endif
00313 int result = inflate(&d->zStream, Z_SYNC_FLUSH);
00314 #ifdef DEBUG_GZIP
00315 kDebug(7005) << " -> inflate returned " << result;
00316 kDebug(7005) << " now avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
00317 kDebug(7005) << " next_in=" << d->zStream.next_in;
00318 #else
00319 if ( result != Z_OK && result != Z_STREAM_END )
00320 kDebug(7005) << "Warning: inflate() returned " << result;
00321 #endif
00322 return ( result == Z_OK ? KFilterBase::Ok : ( result == Z_STREAM_END ? KFilterBase::End : KFilterBase::Error ) );
00323 } else
00324 return uncompress_noop();
00325 }
00326
00327 KGzipFilter::Result KGzipFilter::compress( bool finish )
00328 {
00329 Q_ASSERT ( d->compressed );
00330 Q_ASSERT ( d->mode == QIODevice::WriteOnly );
00331
00332 Bytef* p = d->zStream.next_in;
00333 ulong len = d->zStream.avail_in;
00334 #ifdef DEBUG_GZIP
00335 kDebug(7005) << " calling deflate with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
00336 #endif
00337 const int result = deflate(&d->zStream, finish ? Z_FINISH : Z_NO_FLUSH);
00338 if ( result != Z_OK && result != Z_STREAM_END )
00339 kDebug(7005) << " deflate returned " << result;
00340 if ( d->headerWritten )
00341 {
00342
00343 d->crc = crc32(d->crc, p, len - d->zStream.avail_in);
00344 }
00345 KGzipFilter::Result callerResult = result == Z_OK ? KFilterBase::Ok : (Z_STREAM_END ? KFilterBase::End : KFilterBase::Error);
00346
00347 if (result == Z_STREAM_END && d->headerWritten && !d->footerWritten) {
00348 if (d->zStream.avail_out >= 8 ) {
00349
00350 writeFooter();
00351 } else {
00352
00353
00354 callerResult = KFilterBase::Ok;
00355 }
00356 }
00357 return callerResult;
00358 }