• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KDECore

  • kdecore
  • compression
kgzipfilter.cpp
Go to the documentation of this file.
1/* This file is part of the KDE libraries
2 Copyright (C) 2000-2005 David Faure <faure@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#include "kgzipfilter.h"
21
22#include <time.h>
23#include <zlib.h>
24#include <QDebug>
25#include <QtCore/QIODevice>
26
27
28/* gzip flag byte */
29#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
30#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
31#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
32#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
33#define COMMENT 0x10 /* bit 4 set: file comment present */
34#define RESERVED 0xE0 /* bits 5..7: reserved */
35
36// #define DEBUG_GZIP
37
38class KGzipFilter::Private
39{
40public:
41 Private()
42 : headerWritten(false), footerWritten(false), compressed(false), mode(0), crc(0), isInitialized(false)
43 {
44 zStream.zalloc = (alloc_func)0;
45 zStream.zfree = (free_func)0;
46 zStream.opaque = (voidpf)0;
47 }
48
49 z_stream zStream;
50 bool headerWritten;
51 bool footerWritten;
52 bool compressed;
53 int mode;
54 ulong crc;
55 bool isInitialized;
56};
57
58KGzipFilter::KGzipFilter()
59 : d(new Private)
60{
61}
62
63
64KGzipFilter::~KGzipFilter()
65{
66 delete d;
67}
68
69void KGzipFilter::init(int mode)
70{
71 init(mode, filterFlags() == WithHeaders ? GZipHeader : RawDeflate);
72}
73
74void KGzipFilter::init(int mode, Flag flag)
75{
76 if (d->isInitialized) {
77 terminate();
78 }
79 d->zStream.next_in = Z_NULL;
80 d->zStream.avail_in = 0;
81 if ( mode == QIODevice::ReadOnly )
82 {
83 const int windowBits = (flag == RawDeflate)
84 ? -MAX_WBITS /*no zlib header*/
85 : (flag == GZipHeader) ?
86 MAX_WBITS + 32 /* auto-detect and eat gzip header */
87 : MAX_WBITS /*zlib header*/;
88 const int result = inflateInit2(&d->zStream, windowBits);
89 if ( result != Z_OK ) {
90 qDebug() << "inflateInit2 returned " << result;
91 // TODO return false
92 }
93 } else if ( mode == QIODevice::WriteOnly )
94 {
95 int result = deflateInit2(&d->zStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); // same here
96 if ( result != Z_OK ) {
97 qDebug() << "deflateInit returned " << result;
98 // TODO return false
99 }
100 } else {
101 qWarning() << "KGzipFilter: Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
102 }
103 d->mode = mode;
104 d->compressed = true;
105 d->headerWritten = false;
106 d->footerWritten = false;
107 d->isInitialized = true;
108}
109
110int KGzipFilter::mode() const
111{
112 return d->mode;
113}
114
115void KGzipFilter::terminate()
116{
117 if ( d->mode == QIODevice::ReadOnly )
118 {
119 int result = inflateEnd(&d->zStream);
120 if ( result != Z_OK ) {
121 qDebug() << "inflateEnd returned " << result;
122 // TODO return false
123 }
124 } else if ( d->mode == QIODevice::WriteOnly )
125 {
126 int result = deflateEnd(&d->zStream);
127 if ( result != Z_OK ) {
128 qDebug() << "deflateEnd returned " << result;
129 // TODO return false
130 }
131 }
132 d->isInitialized = false;
133}
134
135
136void KGzipFilter::reset()
137{
138 if ( d->mode == QIODevice::ReadOnly )
139 {
140 int result = inflateReset(&d->zStream);
141 if ( result != Z_OK ) {
142 qDebug() << "inflateReset returned " << result;
143 // TODO return false
144 }
145 } else if ( d->mode == QIODevice::WriteOnly ) {
146 int result = deflateReset(&d->zStream);
147 if ( result != Z_OK ) {
148 qDebug() << "deflateReset returned " << result;
149 // TODO return false
150 }
151 d->headerWritten = false;
152 d->footerWritten = false;
153 }
154}
155
156bool KGzipFilter::readHeader()
157{
158 // We now rely on zlib to read the full header (see the MAX_WBITS + 32 in init).
159 // We just use this method to check if the data is actually compressed.
160
161#ifdef DEBUG_GZIP
162 qDebug() << "avail=" << d->zStream.avail_in;
163#endif
164 // Assume not compressed until we see a gzip header
165 d->compressed = false;
166 Bytef *p = d->zStream.next_in;
167 int i = d->zStream.avail_in;
168 if ((i -= 10) < 0) return false; // Need at least 10 bytes
169#ifdef DEBUG_GZIP
170 qDebug() << "first byte is " << QString::number(*p,16);
171#endif
172 if (*p++ != 0x1f) return false; // GZip magic
173#ifdef DEBUG_GZIP
174 qDebug() << "second byte is " << QString::number(*p,16);
175#endif
176 if (*p++ != 0x8b) return false;
177
178#if 0
179 int method = *p++;
180 int flags = *p++;
181 if ((method != Z_DEFLATED) || (flags & RESERVED) != 0) return false;
182 p += 6;
183 if ((flags & EXTRA_FIELD) != 0) // skip extra field
184 {
185 if ((i -= 2) < 0) return false; // Need at least 2 bytes
186 int len = *p++;
187 len += (*p++) << 8;
188 if ((i -= len) < 0) return false; // Need at least len bytes
189 p += len;
190 }
191 if ((flags & ORIG_NAME) != 0) // skip original file name
192 {
193#ifdef DEBUG_GZIP
194 qDebug() << "ORIG_NAME=" << (char*)p;
195#endif
196 while( (i > 0) && (*p))
197 {
198 i--; p++;
199 }
200 if (--i <= 0) return false;
201 p++;
202 }
203 if ((flags & COMMENT) != 0) // skip comment
204 {
205 while( (i > 0) && (*p))
206 {
207 i--; p++;
208 }
209 if (--i <= 0) return false;
210 p++;
211 }
212 if ((flags & HEAD_CRC) != 0) // skip the header crc
213 {
214 if ((i-=2) < 0) return false;
215 p += 2;
216 }
217
218 d->zStream.avail_in = i;
219 d->zStream.next_in = p;
220#endif
221
222 d->compressed = true;
223#ifdef DEBUG_GZIP
224 qDebug() << "header OK";
225#endif
226 return true;
227}
228
229/* Output a 16 bit value, lsb first */
230#define put_short(w) \
231 *p++ = (uchar) ((w) & 0xff); \
232 *p++ = (uchar) ((ushort)(w) >> 8);
233
234/* Output a 32 bit value to the bit stream, lsb first */
235#define put_long(n) \
236 put_short((n) & 0xffff); \
237 put_short(((ulong)(n)) >> 16);
238
239bool KGzipFilter::writeHeader( const QByteArray & fileName )
240{
241 Bytef *p = d->zStream.next_out;
242 int i = d->zStream.avail_out;
243 *p++ = 0x1f;
244 *p++ = 0x8b;
245 *p++ = Z_DEFLATED;
246 *p++ = ORIG_NAME;
247 put_long( time( 0L ) ); // Modification time (in unix format)
248 *p++ = 0; // Extra flags (2=max compress, 4=fastest compress)
249 *p++ = 3; // Unix
250
251 uint len = fileName.length();
252 for ( uint j = 0 ; j < len ; ++j )
253 *p++ = fileName[j];
254 *p++ = 0;
255 int headerSize = p - d->zStream.next_out;
256 i -= headerSize;
257 Q_ASSERT(i>0);
258 d->crc = crc32(0L, Z_NULL, 0);
259 d->zStream.next_out = p;
260 d->zStream.avail_out = i;
261 d->headerWritten = true;
262 return true;
263}
264
265void KGzipFilter::writeFooter()
266{
267 Q_ASSERT( d->headerWritten );
268 Q_ASSERT(!d->footerWritten);
269 Bytef *p = d->zStream.next_out;
270 int i = d->zStream.avail_out;
271 //qDebug() << "avail_out=" << i << "writing CRC=" << QString::number(d->crc, 16) << "at p=" << p;
272 put_long( d->crc );
273 //qDebug() << "writing totalin=" << d->zStream.total_in << "at p=" << p;
274 put_long( d->zStream.total_in );
275 i -= p - d->zStream.next_out;
276 d->zStream.next_out = p;
277 d->zStream.avail_out = i;
278 d->footerWritten = true;
279}
280
281void KGzipFilter::setOutBuffer( char * data, uint maxlen )
282{
283 d->zStream.avail_out = maxlen;
284 d->zStream.next_out = (Bytef *) data;
285}
286void KGzipFilter::setInBuffer( const char * data, uint size )
287{
288#ifdef DEBUG_GZIP
289 qDebug() << "avail_in=" << size;
290#endif
291 d->zStream.avail_in = size;
292 d->zStream.next_in = (Bytef*) data;
293}
294int KGzipFilter::inBufferAvailable() const
295{
296 return d->zStream.avail_in;
297}
298int KGzipFilter::outBufferAvailable() const
299{
300 return d->zStream.avail_out;
301}
302
303KGzipFilter::Result KGzipFilter::uncompress_noop()
304{
305 // I'm not sure we really need support for that (uncompressed streams),
306 // but why not, it can't hurt to have it. One case I can think of is someone
307 // naming a tar file "blah.tar.gz" :-)
308 if ( d->zStream.avail_in > 0 )
309 {
310 int n = (d->zStream.avail_in < d->zStream.avail_out) ? d->zStream.avail_in : d->zStream.avail_out;
311 memcpy( d->zStream.next_out, d->zStream.next_in, n );
312 d->zStream.avail_out -= n;
313 d->zStream.next_in += n;
314 d->zStream.avail_in -= n;
315 return KFilterBase::Ok;
316 } else
317 return KFilterBase::End;
318}
319
320KGzipFilter::Result KGzipFilter::uncompress()
321{
322#ifndef NDEBUG
323 if (d->mode == 0) {
324 qWarning() << "mode==0; KGzipFilter::init was not called!";
325 return KFilterBase::Error;
326 } else if (d->mode == QIODevice::WriteOnly) {
327 qWarning() << "uncompress called but the filter was opened for writing!";
328 return KFilterBase::Error;
329 }
330 Q_ASSERT ( d->mode == QIODevice::ReadOnly );
331#endif
332
333 if ( d->compressed )
334 {
335#ifdef DEBUG_GZIP
336 qDebug() << "Calling inflate with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
337 qDebug() << " next_in=" << d->zStream.next_in;
338#endif
339 int result = inflate(&d->zStream, Z_SYNC_FLUSH);
340#ifdef DEBUG_GZIP
341 qDebug() << " -> inflate returned " << result;
342 qDebug() << " now avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
343 qDebug() << " next_in=" << d->zStream.next_in;
344#else
345 if ( result != Z_OK && result != Z_STREAM_END )
346 qDebug() << "Warning: inflate() returned " << result;
347#endif
348 return ( result == Z_OK ? KFilterBase::Ok : ( result == Z_STREAM_END ? KFilterBase::End : KFilterBase::Error ) );
349 } else
350 return uncompress_noop();
351}
352
353KGzipFilter::Result KGzipFilter::compress( bool finish )
354{
355 Q_ASSERT ( d->compressed );
356 Q_ASSERT ( d->mode == QIODevice::WriteOnly );
357
358 Bytef* p = d->zStream.next_in;
359 ulong len = d->zStream.avail_in;
360#ifdef DEBUG_GZIP
361 qDebug() << " calling deflate with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
362#endif
363 const int result = deflate(&d->zStream, finish ? Z_FINISH : Z_NO_FLUSH);
364 if ( result != Z_OK && result != Z_STREAM_END ) {
365 qDebug() << " deflate returned " << result;
366 }
367 if ( d->headerWritten )
368 {
369 //qDebug() << "Computing CRC for the next " << len - d->zStream.avail_in << " bytes";
370 d->crc = crc32(d->crc, p, len - d->zStream.avail_in);
371 }
372 KGzipFilter::Result callerResult = result == Z_OK ? KFilterBase::Ok : (Z_STREAM_END ? KFilterBase::End : KFilterBase::Error);
373
374 if (result == Z_STREAM_END && d->headerWritten && !d->footerWritten) {
375 if (d->zStream.avail_out >= 8 /*footer size*/) {
376 //qDebug() << "finished, write footer";
377 writeFooter();
378 } else {
379 // No room to write the footer (#157706/#188415), we'll have to do it on the next pass.
380 //qDebug() << "finished, but no room for footer yet";
381 callerResult = KFilterBase::Ok;
382 }
383 }
384 return callerResult;
385}
KFilterBase::Result
Result
Definition: kfilterbase.h:82
KFilterBase::Error
@ Error
Definition: kfilterbase.h:82
KFilterBase::End
@ End
Definition: kfilterbase.h:82
KFilterBase::Ok
@ Ok
Definition: kfilterbase.h:82
KFilterBase::WithHeaders
@ WithHeaders
Definition: kfilterbase.h:94
KFilterBase::filterFlags
FilterFlags filterFlags() const
Definition: kfilterbase.cpp:162
KGzipFilter::setInBuffer
virtual void setInBuffer(const char *data, uint size)
Definition: kgzipfilter.cpp:286
KGzipFilter::reset
virtual void reset()
Definition: kgzipfilter.cpp:136
KGzipFilter::KGzipFilter
KGzipFilter()
Definition: kgzipfilter.cpp:58
KGzipFilter::compress
virtual Result compress(bool finish)
Definition: kgzipfilter.cpp:353
KGzipFilter::setOutBuffer
virtual void setOutBuffer(char *data, uint maxlen)
Definition: kgzipfilter.cpp:281
KGzipFilter::mode
virtual int mode() const
Definition: kgzipfilter.cpp:110
KGzipFilter::~KGzipFilter
virtual ~KGzipFilter()
Definition: kgzipfilter.cpp:64
KGzipFilter::writeHeader
virtual bool writeHeader(const QByteArray &fileName)
Definition: kgzipfilter.cpp:239
KGzipFilter::Flag
Flag
Definition: kgzipfilter.h:48
KGzipFilter::RawDeflate
@ RawDeflate
Definition: kgzipfilter.h:49
KGzipFilter::GZipHeader
@ GZipHeader
Definition: kgzipfilter.h:51
KGzipFilter::uncompress
virtual Result uncompress()
Definition: kgzipfilter.cpp:320
KGzipFilter::inBufferAvailable
virtual int inBufferAvailable() const
Definition: kgzipfilter.cpp:294
KGzipFilter::writeFooter
void writeFooter()
Definition: kgzipfilter.cpp:265
KGzipFilter::init
virtual void init(int mode)
Definition: kgzipfilter.cpp:69
KGzipFilter::terminate
virtual void terminate()
Definition: kgzipfilter.cpp:115
KGzipFilter::outBufferAvailable
virtual int outBufferAvailable() const
Definition: kgzipfilter.cpp:298
KGzipFilter::readHeader
virtual bool readHeader()
Definition: kgzipfilter.cpp:156
COMMENT
#define COMMENT
Definition: kgzipfilter.cpp:33
EXTRA_FIELD
#define EXTRA_FIELD
Definition: kgzipfilter.cpp:31
HEAD_CRC
#define HEAD_CRC
Definition: kgzipfilter.cpp:30
RESERVED
#define RESERVED
Definition: kgzipfilter.cpp:34
put_long
#define put_long(n)
Definition: kgzipfilter.cpp:235
ORIG_NAME
#define ORIG_NAME
Definition: kgzipfilter.cpp:32
kgzipfilter.h
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDECore

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

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal