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
35namespace 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{
90}
91
92
107{
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{
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
340int 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
virtual ~DeflateOutputStreambuf()
Clean up any resources used by this object.
bool init(FileEntry::CompressionLevel compression_level)
Initialize the zlib library.
uint32_t getCrc32() const
Get the CRC32 of the file.
void endDeflation()
End deflation of current file.
DeflateOutputStreambuf(std::streambuf *outbuf)
Initialize a DeflateOutputStreambuf object.
void flushOutvec()
Flush the cached output data.
virtual int sync()
Synchronize the buffer.
virtual int overflow(int c=EOF)
Handle an overflow.
void closeStream()
Closing the stream.
size_t getSize() const
Retrieve the size of the file deflated.
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
static CompressionLevel const COMPRESSION_LEVEL_MAXIMUM
Definition: fileentry.hpp:92
static CompressionLevel const COMPRESSION_LEVEL_DEFAULT
Definition: fileentry.hpp:87
static CompressionLevel const COMPRESSION_LEVEL_NONE
Definition: fileentry.hpp:90
static CompressionLevel const COMPRESSION_LEVEL_FASTEST
Definition: fileentry.hpp:89
static CompressionLevel const COMPRESSION_LEVEL_SMALLEST
Definition: fileentry.hpp:88
A base class to develop output stream filters.
An IOException is used to signal an I/O error.
Header file that defines zipios::DeflateOutputStreambuf.
The zipios namespace includes the Zipios library definitions.
Definition: backbuffer.cpp:36
size_t getBufferSize()
std::ostringstream OutputStringStream
An output stream using strings.
Various functions used throughout the library.
Various exceptions used throughout the Zipios library, all based on zipios::Exception.