zipios  2.3.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-2022 Made to Order Software Corp. All Rights Reserved
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_invec(getBufferSize())
60  , m_outvec(getBufferSize())
61 {
62  // NOTICE: It is important that this constructor and the methods it
63  // calls do not do anything with the output streambuf m_outbuf.
64  // The reason is that this class can be sub-classed, and the
65  // sub-class should get a chance to write to the buffer first.
66 }
67 
68 
79 {
80  closeStream();
81 }
82 
83 
102 {
103  if(m_zs_initialized)
104  {
105  // This is excluded from the coverage since if we reach this
106  // line there is an internal error that needs to be fixed.
107  throw std::logic_error("DeflateOutputStreambuf::init(): initialization function called when the class is already initialized. This is not supported."); // LCOV_EXCL_LINE
108  }
109  m_zs_initialized = true;
110 
111  int const default_mem_level(8);
112 
113  int zlevel(Z_NO_COMPRESSION);
114  switch(compression_level)
115  {
117  zlevel = Z_DEFAULT_COMPRESSION;
118  break;
119 
121  zlevel = Z_BEST_COMPRESSION;
122  break;
123 
125  zlevel = Z_BEST_SPEED;
126  break;
127 
129  throw std::logic_error("the compression level NONE is not supported in DeflateOutputStreambuf::init()"); // LCOV_EXCL_LINE
130 
131  default:
132  if(compression_level < FileEntry::COMPRESSION_LEVEL_MINIMUM
133  || compression_level > FileEntry::COMPRESSION_LEVEL_MAXIMUM)
134  {
135  // This is excluded from the coverage since if we reach this
136  // line there is an internal error that needs to be fixed.
137  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
138  }
139  // The zlevel is calculated linearly from the user specified value
140  // of 1 to 100
141  //
142  // The calculation goes as follow:
143  //
144  // x = user specified value - 1 (0 to 99)
145  // x = x * 8 (0 to 792)
146  // x = x + 11 / 2 (5 to 797, i.e. +5 with integers)
147  // x = x / 99 (0 to 8)
148  // x = x + 1 (1 to 9)
149  //
150  zlevel = ((compression_level - 1) * 8 + 11 / 2) / 99 + 1;
151  break;
152 
153  }
154 
155  // m_zs.next_in and avail_in must be set according to
156  // zlib.h (inline doc).
157  m_zs.next_in = reinterpret_cast<unsigned char *>(&m_invec[0]);
158  m_zs.avail_in = 0;
159 
160  m_zs.next_out = reinterpret_cast<unsigned char *>(&m_outvec[0]);
161  m_zs.avail_out = getBufferSize();
162 
163  //
164  // windowBits is passed -MAX_WBITS to tell that no zlib
165  // header should be written.
166  //
167  int const err = deflateInit2(&m_zs, zlevel, Z_DEFLATED, -MAX_WBITS, default_mem_level, Z_DEFAULT_STRATEGY);
168  if(err != Z_OK)
169  {
170  // Not too sure how we could generate an error here, the deflateInit2()
171  // would fail if (1) there is not enough memory and (2) if a parameter
172  // is out of wack which neither can be generated from the outside
173  // (well... not easily)
174  std::ostringstream msgs; // LCOV_EXCL_LINE
175  msgs << "DeflateOutputStreambuf::init(): error while initializing zlib, " << zError(err) << std::endl; // LCOV_EXCL_LINE
176  throw IOException(msgs.str()); // LCOV_EXCL_LINE
177  }
178 
179  // streambuf init:
180  setp(&m_invec[0], &m_invec[0] + getBufferSize());
181 
182  m_crc32 = crc32(0, Z_NULL, 0);
183 
184  return err == Z_OK;
185 }
186 
187 
201 {
202  if(m_zs_initialized)
203  {
204  m_zs_initialized = false;
205 
206  // flush any remaining data
207  endDeflation();
208 
209  int const err(deflateEnd(&m_zs));
210  if(err != Z_OK) // when we close a directory, we get the Z_DATA_ERROR!
211  {
212  // There are not too many cases which break the deflateEnd()
213  // function call...
214  std::ostringstream msgs; // LCOV_EXCL_LINE
215  msgs << "DeflateOutputStreambuf::closeStream(): deflateEnd failed: " << zError(err) << std::endl; // LCOV_EXCL_LINE
216  throw IOException(msgs.str()); // LCOV_EXCL_LINE
217  }
218  }
219 }
220 
221 
235 {
236  return m_crc32;
237 }
238 
239 
252 {
253  return m_overflown_bytes;
254 }
255 
256 
271 {
272  int err(Z_OK);
273 
274  m_zs.avail_in = pptr() - pbase();
275  m_zs.next_in = reinterpret_cast<unsigned char *>(&m_invec[0]);
276 
277  if(m_zs.avail_in > 0)
278  {
279  m_crc32 = crc32(m_crc32, m_zs.next_in, m_zs.avail_in); // update crc32
280 
281  m_zs.next_out = reinterpret_cast<unsigned char *>(&m_outvec[0]);
282  m_zs.avail_out = getBufferSize();
283 
284  // Deflate until m_invec is empty.
285  while((m_zs.avail_in > 0 || m_zs.avail_out == 0) && err == Z_OK)
286  {
287  if(m_zs.avail_out == 0)
288  {
289  flushOutvec();
290  }
291 
292  err = deflate(&m_zs, Z_NO_FLUSH);
293  }
294  }
295 
296  // somehow we need this flush here or it fails
297  flushOutvec();
298 
299  // Update 'put' pointers
300  setp(&m_invec[0], &m_invec[0] + getBufferSize());
301 
302  if(err != Z_OK && err != Z_STREAM_END)
303  {
304  // Throw an exception to make istream set badbit
305  //
306  // This is marked as not cover-able by tests because the calls
307  // that access this function only happen in an internal loop and
308  // even if we were to write a direct test, I do not see how
309  // we could end up with an error here
310  OutputStringStream msgs; // LCOV_EXCL_LINE
311  msgs << "Deflation failed:" << zError(err); // LCOV_EXCL_LINE
312  throw IOException(msgs.str()); // LCOV_EXCL_LINE
313  }
314 
315  if(c != EOF)
316  {
317  *pptr() = c;
318  pbump(1);
319  }
320 
321  return 0;
322 }
323 
324 
335 int DeflateOutputStreambuf::sync() // LCOV_EXCL_LINE
336 {
337  return -1; // LCOV_EXCL_LINE
338 }
339 
340 
347 {
353  std::size_t const deflated_bytes(getBufferSize() - m_zs.avail_out);
354  if(deflated_bytes > 0)
355  {
356  std::size_t const bc(m_outbuf->sputn(&m_outvec[0], deflated_bytes));
357  if(deflated_bytes != bc)
358  {
359  // Without implementing our own stream in our test, this
360  // cannot really be reached because it is all happening
361  // inside the same loop in ZipFile::saveCollectionToArchive()
362  throw IOException("DeflateOutputStreambuf::flushOutvec(): write to buffer failed."); // LCOV_EXCL_LINE
363  }
364  }
365 
366  m_zs.next_out = reinterpret_cast<unsigned char *>(&m_outvec[0]);
367  m_zs.avail_out = getBufferSize();
368 }
369 
370 
378 {
379  overflow();
380 
381  m_zs.next_out = reinterpret_cast<unsigned char *>(&m_outvec[0]);
382  m_zs.avail_out = getBufferSize();
383 
384  // Deflate until _invec is empty.
385  int err(Z_OK);
386 
387  // make sure to NOT call deflate() if nothing was written to the
388  // deflate output stream, otherwise we get a "spurious" (as far
389  // Zip archives are concerned) 0x03 0x00 marker from the zlib
390  // library
391  //
392  if(m_overflown_bytes > 0)
393  {
394  while(err == Z_OK)
395  {
396  if(m_zs.avail_out == 0)
397  {
398  flushOutvec();
399  }
400 
401  err = deflate(&m_zs, Z_FINISH);
402  }
403  }
404  else
405  {
406  // this is not expected to happen, but it can
407  err = Z_STREAM_END; // LCOV_EXCL_LINE
408  }
409 
410  flushOutvec();
411 
412  if(err != Z_STREAM_END)
413  {
414  // This is marked as not cover-able because the calls that
415  // access this function only happen in an internal loop and
416  // even if we were to write a direct test, I do not see how
417  // we could end up with an error here
418  std::ostringstream msgs; // LCOV_EXCL_LINE
419  msgs << "DeflateOutputStreambuf::endDeflation(): deflate() failed: " // LCOV_EXCL_LINE
420  << zError(err) << std::endl; // LCOV_EXCL_LINE
421  throw IOException(msgs.str()); // LCOV_EXCL_LINE
422  }
423 }
424 
425 
426 } // namespace
427 
428 // Local Variables:
429 // mode: cpp
430 // indent-tabs-mode: nil
431 // c-basic-offset: 4
432 // tab-width: 4
433 // End:
434 
435 // 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.
size_t getSize() const
Retrieve the size of the file deflated.
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
uint32_t getCrc32() const
Get the CRC32 of the file.
static CompressionLevel const COMPRESSION_LEVEL_FASTEST
Definition: fileentry.hpp:89
static CompressionLevel const COMPRESSION_LEVEL_SMALLEST
Definition: fileentry.hpp:88