• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDECore

kgzipfilter.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2000-2005 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License version 2 as published by the Free Software Foundation.
00007 
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License for more details.
00012 
00013    You should have received a copy of the GNU Library General Public License
00014    along with this library; see the file COPYING.LIB.  If not, write to
00015    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016    Boston, MA 02110-1301, USA.
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 /* gzip flag byte */
00028 #define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
00029 #define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
00030 #define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
00031 #define ORIG_NAME    0x08 /* bit 3 set: original file name present */
00032 #define COMMENT      0x10 /* bit 4 set: file comment present */
00033 #define RESERVED     0xE0 /* bits 5..7: reserved */
00034 
00035 // #define DEBUG_GZIP
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); // windowBits is passed < 0 to suppress zlib header
00075         if ( result != Z_OK )
00076             kDebug(7005) << "inflateInit returned " << result;
00077         // No idea what to do with result :)
00078     } else if ( mode == QIODevice::WriteOnly )
00079     {
00080         int result = deflateInit2(&d->zStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); // same here
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     // Assume not compressed until we successfully decode the header
00133     d->compressed = false;
00134     // Assume the first block of data contains the whole header.
00135     // The right way is to build this as a big state machine which
00136     // is a pain in the ass.
00137     // With 8K-blocks, we don't risk much anyway.
00138     Bytef *p = d->zStream.next_in;
00139     int i = d->zStream.avail_in;
00140     if ((i -= 10)  < 0) return false; // Need at least 10 bytes
00141 #ifdef DEBUG_GZIP
00142     kDebug(7005) << "KGzipFilter::readHeader first byte is " << QString::number(*p,16);
00143 #endif
00144     if (*p++ != 0x1f) return false; // GZip magic
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) // skip extra field
00154     {
00155         if ((i -= 2) < 0) return false; // Need at least 2 bytes
00156         int len = *p++;
00157         len += (*p++) << 8;
00158         if ((i -= len) < 0) return false; // Need at least len bytes
00159         p += len;
00160     }
00161     if ((flags & ORIG_NAME) != 0) // skip original file name
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) // skip comment
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) // skip the header crc
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 /* Output a 16 bit value, lsb first */
00198 #define put_short(w) \
00199     *p++ = (uchar) ((w) & 0xff); \
00200     *p++ = (uchar) ((ushort)(w) >> 8);
00201 
00202 /* Output a 32 bit value to the bit stream, lsb first */
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 ) ); // Modification time (in unix format)
00216     *p++ = 0; // Extra flags (2=max compress, 4=fastest compress)
00217     *p++ = 3; // Unix
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     //kDebug(7005) << "KGzipFilter::writeFooter writing CRC= " << QString::number( d->crc, 16 );
00240     put_long( d->crc );
00241     //kDebug(7005) << "KGzipFilter::writing writing totalin= " << d->zStream.total_in;
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     // I'm not sure we really need support for that (uncompressed streams),
00273     // but why not, it can't hurt to have it. One case I can think of is someone
00274     // naming a tar file "blah.tar.gz" :-)
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         //kDebug(7005) << "Computing CRC for the next " << len - d->zStream.avail_in << " bytes";
00326         d->crc = crc32(d->crc, p, len - d->zStream.avail_in);
00327     }
00328     if ( result == Z_STREAM_END && d->headerWritten )
00329     {
00330         //kDebug(7005) << "KGzipFilter::compress finished, write footer";
00331         writeFooter();
00332     }
00333     return ( result == Z_OK ? KFilterBase::Ok : ( result == Z_STREAM_END ? KFilterBase::End : KFilterBase::Error ) );
00334 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal