zipios++  2.0.2
Zipios++ – a small C++ library that provides easy access to .zip files.
deflateoutputstreambuf.cpp
Go to the documentation of this file.
1 /*
2  Zipios++ - a small C++ library that provides easy access to .zip files.
3 
4  Copyright (C) 2000-2007 Thomas Sondergaard
5  Copyright (C) 2015 Made to Order Software Corporation
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public
9  License as published by the Free Software Foundation; either
10  version 2 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public
18  License along with this library; if not, write to the Free Software
19  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 
29 
31 
32 #include "zipios_common.hpp"
33 
34 
35 namespace zipios
36 {
37 
58  : FilterOutputStreambuf(outbuf)
59  //, m_overflown_bytes(0) -- auto-init
60  , m_invec(getBufferSize())
61  //, m_zs() -- auto-init
62  //, m_zs_initialized(false) -- auto-init
63  , m_outvec(getBufferSize())
64  //, m_crc32(0) -- auto-init
65 {
66  // NOTICE: It is important that this constructor and the methods it
67  // calls does not do anything with the output streambuf m_outbuf.
68  // The reason is that this class can be subclassed, and the
69  // subclass should get a chance to write to the buffer first.
70 
71  // zlib init:
72  m_zs.zalloc = Z_NULL;
73  m_zs.zfree = Z_NULL;
74  m_zs.opaque = Z_NULL;
75 }
76 
77 
88 {
89  closeStream();
90 }
91 
92 
107 {
108  if(m_zs_initialized)
109  {
110  // This is excluded from the coverage since if we reach this
111  // line there is an internal error that needs to be fixed.
112  throw std::logic_error("DeflateOutputStreambuf::init(): initialization function called when the class is already initialized. This is not supported."); // LCOV_EXCL_LINE
113  }
114  m_zs_initialized = true;
115 
116  int const default_mem_level(8);
117 
118  int zlevel(Z_NO_COMPRESSION);
119  switch(compression_level)
120  {
122  zlevel = Z_DEFAULT_COMPRESSION;
123  break;
124 
126  zlevel = Z_BEST_COMPRESSION;
127  break;
128 
130  zlevel = Z_BEST_SPEED;
131  break;
132 
134  throw std::logic_error("the compression level NONE is not supported in DeflateOutputStreambuf::init()"); // LCOV_EXCL_LINE
135 
136  default:
137  if(compression_level < FileEntry::COMPRESSION_LEVEL_MINIMUM
138  || compression_level > FileEntry::COMPRESSION_LEVEL_MAXIMUM)
139  {
140  // This is excluded from the coverage since if we reach this
141  // line there is an internal error that needs to be fixed.
142  throw std::logic_error("the compression level must be defined between -3 and 100, see the zipios++/fileentry.hpp for a list of valid levels."); // LCOV_EXCL_LINE
143  }
144  // The zlevel is calculated linearly from the user specified value
145  // of 1 to 100
146  //
147  // The calculation goes as follow:
148  //
149  // x = user specified value - 1 (0 to 99)
150  // x = x * 8 (0 to 792)
151  // x = x + 11 / 2 (5 to 797, i.e. +5 with integers)
152  // x = x / 99 (0 to 8)
153  // x = x + 1 (1 to 9)
154  //
155  zlevel = ((compression_level - 1) * 8 + 11 / 2) / 99 + 1;
156  break;
157 
158  }
159 
160  // m_zs.next_in and avail_in must be set according to
161  // zlib.h (inline doc).
162  m_zs.next_in = reinterpret_cast<unsigned char *>(&m_invec[0]);
163  m_zs.avail_in = 0;
164 
165  m_zs.next_out = reinterpret_cast<unsigned char *>(&m_outvec[0]);
166  m_zs.avail_out = getBufferSize();
167 
168  //
169  // windowBits is passed -MAX_WBITS to tell that no zlib
170  // header should be written.
171  //
172  int const err = deflateInit2(&m_zs, zlevel, Z_DEFLATED, -MAX_WBITS, default_mem_level, Z_DEFAULT_STRATEGY);
173  if(err != Z_OK)
174  {
175  // Not too sure how we could generate an error here, the deflateInit2()
176  // would fail if (1) there is not enough memory and (2) if a parameter
177  // is out of wack which neither can be generated from the outside
178  // (well... not easily)
179  std::ostringstream msgs; // LCOV_EXCL_LINE
180  msgs << "DeflateOutputStreambuf::init(): error while initializing zlib, " << zError(err) << std::endl; // LCOV_EXCL_LINE
181  throw IOException(msgs.str()); // LCOV_EXCL_LINE
182  }
183 
184  // streambuf init:
185  setp(&m_invec[0], &m_invec[0] + getBufferSize());
186 
187  m_crc32 = crc32(0, Z_NULL, 0);
188 
189  return err == Z_OK;
190 }
191 
192 
206 {
207  if(m_zs_initialized)
208  {
209  m_zs_initialized = false;
210 
211  // flush any remaining data
212  endDeflation();
213 
214  int const err(deflateEnd(&m_zs));
215  if(err != Z_OK) // when we close a directory, we get the Z_DATA_ERROR!
216  {
217  // There are not too many cases which break the deflateEnd()
218  // function call...
219  std::ostringstream msgs; // LCOV_EXCL_LINE
220  msgs << "DeflateOutputStreambuf::closeStream(): deflateEnd failed: " << zError(err) << std::endl; // LCOV_EXCL_LINE
221  throw IOException(msgs.str()); // LCOV_EXCL_LINE
222  }
223  }
224 }
225 
226 
240 {
241  return m_crc32;
242 }
243 
244 
257 {
258  return m_overflown_bytes;
259 }
260 
261 
276 {
277  int err(Z_OK);
278 
279  m_zs.avail_in = pptr() - pbase();
280  m_zs.next_in = reinterpret_cast<unsigned char *>(&m_invec[0]);
281 
282  if(m_zs.avail_in > 0)
283  {
284  m_crc32 = crc32(m_crc32, m_zs.next_in, m_zs.avail_in); // update crc32
285 
286  m_zs.next_out = reinterpret_cast<unsigned char *>(&m_outvec[0]);
287  m_zs.avail_out = getBufferSize();
288 
289  // Deflate until m_invec is empty.
290  while((m_zs.avail_in > 0 || m_zs.avail_out == 0) && err == Z_OK)
291  {
292  if(m_zs.avail_out == 0)
293  {
294  flushOutvec();
295  }
296 
297  err = deflate(&m_zs, Z_NO_FLUSH);
298  }
299  }
300 
301  // somehow we need this flush here or it fails
302  flushOutvec();
303 
304  // Update 'put' pointers
305  setp(&m_invec[0], &m_invec[0] + getBufferSize());
306 
307  if(err != Z_OK && err != Z_STREAM_END)
308  {
309  // Throw an exception to make istream set badbit
310  //
311  // This is marked as not cover-able because the calls that
312  // access this function only happen in an internal loop and
313  // even if we were to write a direct test, I do not see how
314  // we could end up with an error here
315  OutputStringStream msgs; // LCOV_EXCL_LINE
316  msgs << "Deflation failed:" << zError(err); // LCOV_EXCL_LINE
317  throw IOException(msgs.str()); // LCOV_EXCL_LINE
318  }
319 
320  if(c != EOF)
321  {
322  *pptr() = c;
323  pbump(1);
324  }
325 
326  return 0;
327 }
328 
329 
340 int DeflateOutputStreambuf::sync() // LCOV_EXCL_LINE
341 {
342  return -1; // LCOV_EXCL_LINE
343 }
344 
345 
352 {
358  size_t deflated_bytes(getBufferSize() - m_zs.avail_out);
359  if(deflated_bytes > 0)
360  {
361  size_t const bc(m_outbuf->sputn(&m_outvec[0], deflated_bytes));
362  if(deflated_bytes != bc)
363  {
364  // Without implementing our own stream in our test, this
365  // cannot really be reached because it is all happening
366  // inside the same loop in ZipFile::saveCollectionToArchive()
367  throw IOException("DeflateOutputStreambuf::flushOutvec(): write to buffer failed."); // LCOV_EXCL_LINE
368  }
369  }
370 
371  m_zs.next_out = reinterpret_cast<unsigned char *>(&m_outvec[0]);
372  m_zs.avail_out = getBufferSize();
373 }
374 
375 
383 {
384  overflow();
385 
386  m_zs.next_out = reinterpret_cast<unsigned char *>(&m_outvec[0]);
387  m_zs.avail_out = getBufferSize();
388 
389  // Deflate until _invec is empty.
390  int err(Z_OK);
391 
392  // make sure to NOT call deflate() if nothing was written to the
393  // deflate output stream, otherwise we get a "spurious" (as far
394  // Zip archives are concerned) 0x03 0x00 marker from the zlib
395  // library
396  //
397  if(m_overflown_bytes > 0)
398  {
399  while(err == Z_OK)
400  {
401  if(m_zs.avail_out == 0)
402  {
403  flushOutvec();
404  }
405 
406  err = deflate(&m_zs, Z_FINISH);
407  }
408  }
409  else
410  {
411  // this is not expected to happen, but it can
412  err = Z_STREAM_END; // LCOV_EXCL_LINE
413  }
414 
415  flushOutvec();
416 
417  if(err != Z_STREAM_END)
418  {
419  // This is marked as not cover-able because the calls that
420  // access this function only happen in an internal loop and
421  // even if we were to write a direct test, I do not see how
422  // we could end up with an error here
423  std::ostringstream msgs; // LCOV_EXCL_LINE
424  msgs << "DeflateOutputStreambuf::endDeflation(): deflate() failed: " // LCOV_EXCL_LINE
425  << zError(err) << std::endl; // LCOV_EXCL_LINE
426  throw IOException(msgs.str()); // LCOV_EXCL_LINE
427  }
428 }
429 
430 
431 } // namespace
432 
433 // Local Variables:
434 // mode: cpp
435 // indent-tabs-mode: nil
436 // c-basic-offset: 4
437 // tab-width: 4
438 // End:
439 
440 // vim: ts=4 sw=4 et
A base class to develop output stream filters.
Various exceptions used throughout the Zipios++ library, all based on zipios::Exception.
virtual int overflow(int c=EOF)
Handle an overflow.
void closeStream()
Closing the stream.
void endDeflation()
End deflation of current file.
virtual int sync()
Synchronize the buffer.
bool init(FileEntry::CompressionLevel compression_level)
Initialize the zlib library.
static CompressionLevel const COMPRESSION_LEVEL_NONE
Definition: fileentry.hpp:90
static CompressionLevel const COMPRESSION_LEVEL_DEFAULT
Definition: fileentry.hpp:87
int CompressionLevel
The compression level to be used to save an entry.
Definition: fileentry.hpp:85
static CompressionLevel const COMPRESSION_LEVEL_MINIMUM
Definition: fileentry.hpp:91
An IOException is used to signal an I/O error.
Header file that defines zipios::DeflateOutputStreambuf.
void flushOutvec()
Flush the cached output data.
DeflateOutputStreambuf(std::streambuf *outbuf)
Initialize a DeflateOutputStreambuf object.
virtual ~DeflateOutputStreambuf()
Clean up any resources used by this object.
size_t getBufferSize()
Various functions used throughout the library.
std::ostringstream OutputStringStream
An output stream using strings.
static CompressionLevel const COMPRESSION_LEVEL_MAXIMUM
Definition: fileentry.hpp:92
static CompressionLevel const COMPRESSION_LEVEL_FASTEST
Definition: fileentry.hpp:89
static CompressionLevel const COMPRESSION_LEVEL_SMALLEST
Definition: fileentry.hpp:88
size_t getSize() const
Retrieve the size of the file deflated.
uint32_t getCrc32() const
Get the CRC32 of the file.