LCOV - code coverage report
Current view: top level - src - inflateinputstreambuf.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 45 45 100.0 %
Date: 2019-04-24 14:10:30 Functions: 6 7 85.7 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      22             : /** \file
      23             :  * \brief Implementation of zipios::InflateInputStreambuf.
      24             :  *
      25             :  * This file defines the various functions found in the
      26             :  * zipios::InflateInputStreambuf class. It filters a stream buffer
      27             :  * to decompress data that was compressed using the zlib library.
      28             :  */
      29             : 
      30             : #include "inflateinputstreambuf.hpp"
      31             : 
      32             : #include "zipios/zipiosexceptions.hpp"
      33             : 
      34             : #include "zipios_common.hpp"
      35             : 
      36             : 
      37             : namespace zipios
      38             : {
      39             : 
      40             : /** \class InflateInputStreambuf
      41             :  * \brief A stream buffer to inflate data previous compressed with zlib.
      42             :  *
      43             :  * The InflateInputStreambuf class is an input stream filter, that
      44             :  * inflates the input from the attached input stream.
      45             :  *
      46             :  * Deflation/Inflation is a compression/decompression method used
      47             :  * in gzip and zip. The zlib library is used to perform the actual
      48             :  * inflation, this class only wraps the functionality in an input
      49             :  * stream filter.
      50             :  *
      51             :  * \todo
      52             :  * Add support for bzip2, lzma compressions.
      53             :  */
      54             : 
      55             : 
      56             : 
      57             : /** \brief Initialize a InflateInputStreambuf.
      58             :  *
      59             :  * The constructor initializes the various stream buffers and
      60             :  * setup the stream start position using the \p start_pos
      61             :  * parameter.
      62             :  *
      63             :  * Data will be inflated (decompressed using zlib) before being
      64             :  * returned.
      65             :  *
      66             :  * \param[in,out] inbuf  The streambuf to use for input.
      67             :  * \param[in] start_pos  A position to reset the inbuf to before reading. Specify
      68             :  *                       -1 to not change the position.
      69             :  */
      70       96763 : InflateInputStreambuf::InflateInputStreambuf(std::streambuf *inbuf, offset_t start_pos)
      71             :     : FilterInputStreambuf(inbuf)
      72             :     , m_outvec(getBufferSize())
      73       96763 :     , m_invec(getBufferSize())
      74             :     //, m_zs() -- auto-init
      75             :     //, m_zs_initialized(false) -- auto-init
      76             : {
      77             :     // NOTICE: It is important that this constructor and the methods it
      78             :     // calls doesn't do anything with the input streambuf inbuf, other
      79             :     // than repositioning it to the specified position. The reason is
      80             :     // that this class can be subclassed, and the subclass should get a
      81             :     // chance to read from the buffer first)
      82             : 
      83             :     // zlib init:
      84       96763 :     m_zs.zalloc = Z_NULL;
      85       96763 :     m_zs.zfree  = Z_NULL;
      86       96763 :     m_zs.opaque = Z_NULL;
      87             : 
      88       96763 :     reset(start_pos);
      89             :     // We are not checking the return value of reset() and throwing
      90             :     // an exception in case of an error, because we cannot catch the exception
      91             :     // in the constructors of subclasses with all compilers.
      92       96763 : }
      93             : 
      94             : 
      95             : /** \brief Clean up the InflateInputStreambuf object.
      96             :  *
      97             :  * The destructor makes sure all allocated resources get cleaned up.
      98             :  */
      99      193526 : InflateInputStreambuf::~InflateInputStreambuf()
     100             : {
     101             :     // Dealloc z_stream stuff
     102       96763 :     int const err(inflateEnd(&m_zs));
     103       96763 :     if(err != Z_OK)
     104             :     {
     105             :         // in a destructor we cannot throw...
     106             :         OutputStringStream msgs; // LCOV_EXCL_LINE
     107             :         msgs << "InflateInputStreambuf::~InflateInputStreambuf(): inflateEnd() failed" // LCOV_EXCL_LINE
     108             :              << ": " << zError(err); // LCOV_EXCL_LINE
     109             :         /** \TODO
     110             :          * Write an error callback interface and call that instead of
     111             :          * using std::cerr...
     112             :          */
     113             :         std::cerr << msgs.str() << std::endl; // LCOV_EXCL_LINE
     114             :     }
     115       96763 : }
     116             : 
     117             : 
     118             : /** \brief Called when more data is required.
     119             :  *
     120             :  * The function ensures that at least one byte is available
     121             :  * in the input area by updating the pointers to the input area
     122             :  * and reading more data in from the input sequence if required.
     123             :  *
     124             :  * This function actually passes the data through the zlib library
     125             :  * to decompress it.
     126             :  *
     127             :  * \return The value of that character on success or
     128             :  *         std::streambuf::traits_type::eof() on failure.
     129             :  */
     130      763145 : std::streambuf::int_type InflateInputStreambuf::underflow()
     131             : {
     132             :     // If not really underflow do not fill buffer
     133             :     // (is that really possible?!)
     134      763145 :     if(gptr() < egptr())
     135             :     {
     136             :         return traits_type::to_int_type(*gptr()); // LCOV_EXCL_LINE
     137             :     }
     138             : 
     139             :     // Prepare _outvec and get array pointers
     140      763145 :     m_zs.avail_out = getBufferSize();
     141      763145 :     m_zs.next_out = reinterpret_cast<unsigned char *>(&m_outvec[0]);
     142             : 
     143             :     // Inflate until _outvec is full
     144             :     // eof (or I/O prob) on _inbuf will break out of loop too.
     145      763145 :     int err(Z_OK);
     146     3437901 :     while(m_zs.avail_out > 0 && err == Z_OK)
     147             :     {
     148     1337378 :         if(m_zs.avail_in == 0)
     149             :         {
     150             :             // fill m_invec
     151      668901 :             std::streamsize const bc(m_inbuf->sgetn(&m_invec[0], getBufferSize()));
     152             :             /** \FIXME
     153             :              * Add I/O error handling while inflating data from a file.
     154             :              */
     155      668901 :             m_zs.next_in = reinterpret_cast<unsigned char *>(&m_invec[0]);
     156      668901 :             m_zs.avail_in = bc;
     157             :             // If we could not read any new data (bc == 0) and inflate is not
     158             :             // done it will return Z_BUF_ERROR and thus breaks out of the
     159             :             // loop. This means we do not have to respond to the situation
     160             :             // where we cannot read more bytes here.
     161             :         }
     162             : 
     163     1337378 :         err = inflate(&m_zs, Z_NO_FLUSH);
     164             :     }
     165             : 
     166             :     // Normally the number of inflated bytes will be the
     167             :     // full length of the output buffer, but if we can't read
     168             :     // more input from the _inbuf streambuf, we end up with
     169             :     // less.
     170      763145 :     offset_t const inflated_bytes = getBufferSize() - m_zs.avail_out;
     171      763145 :     setg(&m_outvec[0], &m_outvec[0], &m_outvec[0] + inflated_bytes);
     172             : 
     173             :     /** \FIXME
     174             :      * Look at the error returned from inflate here, if there is
     175             :      * some way to report it to the InflateInputStreambuf user.
     176             :      * Until I find out I'll just print a warning to stdout.
     177             :      * This at least throws, we probably want to create a log
     178             :      * mechanism that the end user can connect to with a callback.
     179             :      */
     180      763145 :     if(err != Z_OK && err != Z_STREAM_END)
     181             :     {
     182          20 :         OutputStringStream msgs;
     183          10 :         msgs << "InflateInputStreambuf::underflow(): inflate failed"
     184          20 :              << ": " << zError(err);
     185             :         // Throw an exception to immediately exit to the read() or similar
     186             :         // function and make istream set badbit
     187          10 :         throw IOException(msgs.str());
     188             :     }
     189             : 
     190      763135 :     if(inflated_bytes > 0)
     191             :     {
     192      668477 :         return traits_type::to_int_type(*gptr());
     193             :     }
     194             : 
     195       94658 :     return traits_type::eof();
     196             : }
     197             : 
     198             : 
     199             : 
     200             : /** \brief Initializes the stream buffer.
     201             :  *
     202             :  * This function resets the zlib stream and purges input and output buffers.
     203             :  * It also repositions the input streambuf at stream_position.
     204             :  *
     205             :  * \warning
     206             :  * This method is called in the constructor, so it must not read anything
     207             :  * from the input streambuf m_inbuf (see notice in constructor.)
     208             :  *
     209             :  * \param[in] stream_position  A position to reset the inbuf to before
     210             :  *                             reading. Specify -1 to read from the
     211             :  *                             current position.
     212             :  *
     213             :  * \sa InflateInputStreambuf()
     214             :  */
     215      191431 : bool InflateInputStreambuf::reset(offset_t stream_position)
     216             : {
     217      191431 :     if(stream_position >= 0)
     218             :     {
     219             :         // reposition m_inbuf
     220       96763 :         m_inbuf->pubseekpos(stream_position);
     221             :     }
     222             : 
     223             :     // m_zs.next_in and avail_in must be set according to
     224             :     // zlib.h (inline doc).
     225      191431 :     m_zs.next_in = reinterpret_cast<Bytef *>(&m_invec[0]);
     226      191431 :     m_zs.avail_in = 0;
     227             : 
     228      191431 :     int err(Z_OK);
     229      191431 :     if(m_zs_initialized)
     230             :     {
     231             :         // just reset it
     232       94668 :         err = inflateReset(&m_zs);
     233             :     }
     234             :     else
     235             :     {
     236             :         // initialize it
     237       96763 :         err = inflateInit2(&m_zs, -MAX_WBITS);
     238             :         /* windowBits is passed < 0 to tell that there is no zlib header.
     239             :            Note that in this case inflate *requires* an extra "dummy" byte
     240             :            after the compressed stream in order to complete decompression
     241             :            and return Z_STREAM_END.  We always have an extra "dummy" byte,
     242             :            because there is always some trailing data after the compressed
     243             :            data (either the next entry or the central directory.  */
     244       96763 :         m_zs_initialized = true;
     245             :     }
     246             : 
     247             :     // streambuf init:
     248             :     // The important thing here, is that
     249             :     // - the pointers are not NULL (which would mean unbuffered)
     250             :     // - and that gptr() is not less than egptr() (so we trigger underflow
     251             :     //   the first time data is read).
     252      191431 :     setg(&m_outvec[0], &m_outvec[0] + getBufferSize(), &m_outvec[0] + getBufferSize());
     253             : 
     254      191431 :     return err == Z_OK;
     255             : }
     256             : 
     257             : 
     258           3 : } // zipios namespace
     259             : 
     260             : // Local Variables:
     261             : // mode: cpp
     262             : // indent-tabs-mode: nil
     263             : // c-basic-offset: 4
     264             : // tab-width: 4
     265             : // End:
     266             : 
     267             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12