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