LCOV - code coverage report
Current view: top level - src - inflateinputstreambuf.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 42 43 97.7 %
Date: 2024-06-15 08:26:09 Functions: 4 5 80.0 %
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-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             : 
      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       53061 : InflateInputStreambuf::InflateInputStreambuf(std::streambuf * inbuf, offset_t start_pos)
      71             :     : FilterInputStreambuf(inbuf)
      72       53061 :     , m_outvec(getBufferSize())
      73      106122 :     , m_invec(getBufferSize())
      74             : {
      75             :     // NOTICE: It is important that this constructor and the methods it
      76             :     // calls doesn't do anything with the input streambuf inbuf, other
      77             :     // than repositioning it to the specified \p start_pos. The reason is
      78             :     // that this class can be sub-classed, and the sub-class should get a
      79             :     // chance to read from the buffer first
      80             : 
      81       53061 :     reset(start_pos);
      82             :     // We are not checking the return value of reset() and throwing
      83             :     // an exception in case of an error, because we cannot catch the
      84             :     // exception in the constructors of sub-classes with all compilers.
      85       53061 : }
      86             : 
      87             : 
      88             : /** \brief Clean up the InflateInputStreambuf object.
      89             :  *
      90             :  * The destructor makes sure all allocated resources get cleaned up.
      91             :  */
      92       53061 : InflateInputStreambuf::~InflateInputStreambuf()
      93             : {
      94             :     // Dealloc z_stream stuff
      95       53061 :     int const err(inflateEnd(&m_zs));
      96       53061 :     if(err != Z_OK)
      97             :     {
      98             :         // in a destructor we cannot throw...
      99             :         OutputStringStream msgs; // LCOV_EXCL_LINE
     100             :         msgs << "InflateInputStreambuf::~InflateInputStreambuf(): inflateEnd() failed" // LCOV_EXCL_LINE
     101             :              << ": " << zError(err); // LCOV_EXCL_LINE
     102             :         /** \TODO
     103             :          * Write an error callback interface and call that instead of
     104             :          * using std::cerr...
     105             :          */
     106             :         std::cerr << msgs.str() << std::endl; // LCOV_EXCL_LINE
     107           0 :     }
     108       53061 : }
     109             : 
     110             : 
     111             : /** \brief Called when more data is required.
     112             :  *
     113             :  * The function ensures that at least one byte is available
     114             :  * in the input area by updating the pointers to the input area
     115             :  * and reading more data in from the input sequence if required.
     116             :  *
     117             :  * This function actually passes the data through the zlib library
     118             :  * to decompress it.
     119             :  *
     120             :  * \return The value of that character on success or
     121             :  *         std::streambuf::traits_type::eof() on failure.
     122             :  */
     123      399682 : std::streambuf::int_type InflateInputStreambuf::underflow()
     124             : {
     125             :     // If not really underflow do not fill buffer
     126             :     // (is that really possible?!)
     127      399682 :     if(gptr() < egptr())
     128             :     {
     129             :         return traits_type::to_int_type(*gptr()); // LCOV_EXCL_LINE
     130             :     }
     131             : 
     132             :     // Prepare _outvec and get array pointers
     133      399682 :     m_zs.avail_out = getBufferSize();
     134      399682 :     m_zs.next_out = reinterpret_cast<unsigned char *>(&m_outvec[0]);
     135             : 
     136             :     // Inflate until _outvec is full
     137             :     // eof (or I/O prob) on _inbuf will break out of loop too.
     138      399682 :     int err(Z_OK);
     139     1097747 :     while(m_zs.avail_out > 0 && err == Z_OK)
     140             :     {
     141      698065 :         if(m_zs.avail_in == 0)
     142             :         {
     143             :             // fill m_invec
     144      349141 :             std::streamsize const bc(m_inbuf->sgetn(&m_invec[0], getBufferSize()));
     145             :             /** \FIXME
     146             :              * Add I/O error handling while inflating data from a file.
     147             :              */
     148      349141 :             m_zs.next_in = reinterpret_cast<unsigned char *>(&m_invec[0]);
     149      349141 :             m_zs.avail_in = bc;
     150             :             // If we could not read any new data (bc == 0) and inflate is not
     151             :             // done it will return Z_BUF_ERROR and thus breaks out of the
     152             :             // loop. This means we do not have to respond to the situation
     153             :             // where we cannot read more bytes here.
     154             :         }
     155             : 
     156      698065 :         err = inflate(&m_zs, Z_NO_FLUSH);
     157             :     }
     158             : 
     159             :     // Normally the number of inflated bytes will be the
     160             :     // full length of the output buffer, but if we can't read
     161             :     // more input from the _inbuf streambuf, we end up with
     162             :     // less.
     163      399682 :     offset_t const inflated_bytes = getBufferSize() - m_zs.avail_out;
     164      399682 :     setg(&m_outvec[0], &m_outvec[0], &m_outvec[0] + inflated_bytes);
     165             : 
     166             :     /** \FIXME
     167             :      * Look at the error returned from inflate here, if there is
     168             :      * some way to report it to the InflateInputStreambuf user.
     169             :      * Until I find out I'll just print a warning to stdout.
     170             :      * This at least throws, we probably want to create a log
     171             :      * mechanism that the end user can connect to with a callback.
     172             :      */
     173      399682 :     if(err != Z_OK && err != Z_STREAM_END)
     174             :     {
     175          10 :         OutputStringStream msgs;
     176             :         msgs << "InflateInputStreambuf::underflow(): inflate failed"
     177          10 :              << ": " << zError(err);
     178             :         // Throw an exception to immediately exit to the read() or similar
     179             :         // function and make istream set badbit
     180          10 :         throw IOException(msgs.str());
     181          10 :     }
     182             : 
     183      399672 :     if(inflated_bytes > 0)
     184             :     {
     185      348924 :         return traits_type::to_int_type(*gptr());
     186             :     }
     187             : 
     188       50748 :     return traits_type::eof();
     189             : }
     190             : 
     191             : 
     192             : 
     193             : /** \brief Initializes the stream buffer.
     194             :  *
     195             :  * This function resets the zlib stream and purges input and output buffers.
     196             :  * It also repositions the input streambuf at stream_position.
     197             :  *
     198             :  * \warning
     199             :  * This method is called in the constructor, so it must not read anything
     200             :  * from the input streambuf m_inbuf (see notice in constructor.)
     201             :  *
     202             :  * \param[in] stream_position  A position to reset the inbuf to before
     203             :  *                             reading. Specify -1 to read from the
     204             :  *                             current position.
     205             :  *
     206             :  * \sa InflateInputStreambuf()
     207             :  */
     208      103819 : bool InflateInputStreambuf::reset(offset_t stream_position)
     209             : {
     210      103819 :     if(stream_position >= 0)
     211             :     {
     212             :         // reposition m_inbuf
     213       53061 :         m_inbuf->pubseekpos(stream_position);
     214             :     }
     215             : 
     216             :     // m_zs.next_in and avail_in must be set according to
     217             :     // zlib.h (inline doc).
     218      103819 :     m_zs.next_in = reinterpret_cast<Bytef *>(&m_invec[0]);
     219      103819 :     m_zs.avail_in = 0;
     220             : 
     221      103819 :     int err(Z_OK);
     222      103819 :     if(m_zs_initialized)
     223             :     {
     224             :         // just reset it
     225       50758 :         err = inflateReset(&m_zs);
     226             :     }
     227             :     else
     228             :     {
     229             :         // initialize it
     230       53061 :         err = inflateInit2(&m_zs, -MAX_WBITS);
     231             :         /* windowBits is passed < 0 to tell that there is no zlib header.
     232             :            Note that in this case inflate *requires* an extra "dummy" byte
     233             :            after the compressed stream in order to complete decompression
     234             :            and return Z_STREAM_END.  We always have an extra "dummy" byte,
     235             :            because there is always some trailing data after the compressed
     236             :            data (either the next entry or the central directory.  */
     237       53061 :         m_zs_initialized = true;
     238             :     }
     239             : 
     240             :     // streambuf init:
     241             :     // The important thing here, is that
     242             :     // - the pointers are not NULL (which would mean unbuffered)
     243             :     // - and that gptr() is not less than egptr() (so we trigger underflow
     244             :     //   the first time data is read).
     245      103819 :     setg(&m_outvec[0], &m_outvec[0] + getBufferSize(), &m_outvec[0] + getBufferSize());
     246             : 
     247      103819 :     return err == Z_OK;
     248             : }
     249             : 
     250             : 
     251             : } // zipios namespace
     252             : 
     253             : // Local Variables:
     254             : // mode: cpp
     255             : // indent-tabs-mode: nil
     256             : // c-basic-offset: 4
     257             : // tab-width: 4
     258             : // End:
     259             : 
     260             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14

Snap C++ | List of projects | List of versions