zipios 2.3.4
Zipios -- a small C++ library providing 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
35namespace 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
82
83
102{
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{
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
335int 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
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.
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.
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.