• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.36 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 
38 class KGzipFilter::Private
39 {
40 public:
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 
58 KGzipFilter::KGzipFilter()
59  : d(new Private)
60 {
61 }
62 
63 
64 KGzipFilter::~KGzipFilter()
65 {
66  delete d;
67 }
68 
69 void KGzipFilter::init(int mode)
70 {
71  init(mode, filterFlags() == WithHeaders ? GZipHeader : RawDeflate);
72 }
73 
74 void 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 
110 int KGzipFilter::mode() const
111 {
112  return d->mode;
113 }
114 
115 void 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 
136 void 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 
156 bool 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 
239 bool 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 
265 void 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 
281 void KGzipFilter::setOutBuffer( char * data, uint maxlen )
282 {
283  d->zStream.avail_out = maxlen;
284  d->zStream.next_out = (Bytef *) data;
285 }
286 void 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 }
294 int KGzipFilter::inBufferAvailable() const
295 {
296  return d->zStream.avail_in;
297 }
298 int KGzipFilter::outBufferAvailable() const
299 {
300  return d->zStream.avail_out;
301 }
302 
303 KGzipFilter::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 
320 KGzipFilter::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 
353 KGzipFilter::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 }
KGzipFilter::RawDeflate
Definition: kgzipfilter.h:49
HEAD_CRC
#define HEAD_CRC
Definition: kgzipfilter.cpp:30
KGzipFilter::GZipHeader
Definition: kgzipfilter.h:51
KGzipFilter::setInBuffer
virtual void setInBuffer(const char *data, uint size)
Definition: kgzipfilter.cpp:286
KGzipFilter::KGzipFilter
KGzipFilter()
Definition: kgzipfilter.cpp:58
EXTRA_FIELD
#define EXTRA_FIELD
Definition: kgzipfilter.cpp:31
KGzipFilter::~KGzipFilter
virtual ~KGzipFilter()
Definition: kgzipfilter.cpp:64
KGzipFilter::setOutBuffer
virtual void setOutBuffer(char *data, uint maxlen)
Definition: kgzipfilter.cpp:281
kgzipfilter.h
COMMENT
#define COMMENT
Definition: kgzipfilter.cpp:33
KGzipFilter::terminate
virtual void terminate()
Definition: kgzipfilter.cpp:115
KGzipFilter::compress
virtual Result compress(bool finish)
Definition: kgzipfilter.cpp:353
KGzipFilter::Flag
Flag
Definition: kgzipfilter.h:48
KFilterBase::End
Definition: kfilterbase.h:82
KGzipFilter::reset
virtual void reset()
Definition: kgzipfilter.cpp:136
KGzipFilter::writeFooter
void writeFooter()
Definition: kgzipfilter.cpp:265
KGzipFilter::outBufferAvailable
virtual int outBufferAvailable() const
Definition: kgzipfilter.cpp:298
KGzipFilter::uncompress
virtual Result uncompress()
Definition: kgzipfilter.cpp:320
KGzipFilter::mode
virtual int mode() const
Definition: kgzipfilter.cpp:110
KGzipFilter::init
virtual void init(int mode)
Definition: kgzipfilter.cpp:69
put_long
#define put_long(n)
Definition: kgzipfilter.cpp:235
KGzipFilter::writeHeader
virtual bool writeHeader(const QByteArray &fileName)
Definition: kgzipfilter.cpp:239
KFilterBase::Result
Result
Definition: kfilterbase.h:82
ORIG_NAME
#define ORIG_NAME
Definition: kgzipfilter.cpp:32
RESERVED
#define RESERVED
Definition: kgzipfilter.cpp:34
KFilterBase::Ok
Definition: kfilterbase.h:82
KFilterBase::filterFlags
FilterFlags filterFlags() const
Definition: kfilterbase.cpp:162
KFilterBase::WithHeaders
Definition: kfilterbase.h:94
KGzipFilter::inBufferAvailable
virtual int inBufferAvailable() const
Definition: kgzipfilter.cpp:294
KFilterBase::Error
Definition: kfilterbase.h:82
KGzipFilter::readHeader
virtual bool readHeader()
Definition: kgzipfilter.cpp:156
This file is part of the KDE documentation.
Documentation copyright © 1996-2017 The KDE developers.
Generated on Sat Sep 30 2017 14:32:59 by doxygen 1.8.13 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.36 API Reference

Skip menu "kdelibs-4.14.36 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