zipios  2.2.0
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-2019 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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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: (this is done in the class declaration)
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
The zipios namespace includes the Zipios library definitions.
Definition: backbuffer.cpp:35
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.