LCOV - code coverage report
Current view: top level - source/lib/file/archive - codec_zlib.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 126 0.8 %
Date: 2023-01-19 00:18:29 Functions: 2 37 5.4 %

          Line data    Source code
       1             : /* Copyright (C) 2020 Wildfire Games.
       2             :  *
       3             :  * Permission is hereby granted, free of charge, to any person obtaining
       4             :  * a copy of this software and associated documentation files (the
       5             :  * "Software"), to deal in the Software without restriction, including
       6             :  * without limitation the rights to use, copy, modify, merge, publish,
       7             :  * distribute, sublicense, and/or sell copies of the Software, and to
       8             :  * permit persons to whom the Software is furnished to do so, subject to
       9             :  * the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included
      12             :  * in all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      15             :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      16             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      17             :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      18             :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      19             :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      20             :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      21             :  */
      22             : 
      23             : #include "precompiled.h"
      24             : #include "lib/file/archive/codec_zlib.h"
      25             : 
      26             : #include "lib/alignment.h"
      27             : #include "lib/file/archive/codec.h"
      28             : #include "lib/external_libraries/zlib.h"
      29             : 
      30             : #include "lib/sysdep/cpu.h"
      31             : 
      32             : #include <cstring>
      33             : 
      34           0 : class Codec_ZLib : public ICodec
      35             : {
      36             : public:
      37           0 :     u32 UpdateChecksum(u32 checksum, const u8* in, size_t inSize) const
      38             :     {
      39             : #if CODEC_COMPUTE_CHECKSUM
      40           0 :         return (u32)crc32(checksum, in, (uInt)inSize);
      41             : #else
      42             :         UNUSED2(checksum);
      43             :         UNUSED2(in);
      44             :         UNUSED2(inSize);
      45             :         return 0;
      46             : #endif
      47             :     }
      48             : 
      49             : protected:
      50           0 :     u32 InitializeChecksum()
      51             :     {
      52             : #if CODEC_COMPUTE_CHECKSUM
      53           0 :         return crc32(0, 0, 0);
      54             : #else
      55             :         return 0;
      56             : #endif
      57             :     }
      58             : };
      59             : 
      60             : 
      61             : //-----------------------------------------------------------------------------
      62             : 
      63             : class Codec_ZLibNone : public Codec_ZLib
      64             : {
      65             : public:
      66           0 :     Codec_ZLibNone()
      67           0 :     {
      68           0 :         Reset();
      69           0 :     }
      70             : 
      71           0 :     virtual ~Codec_ZLibNone()
      72           0 :     {
      73           0 :     }
      74             : 
      75           0 :     virtual size_t MaxOutputSize(size_t inSize) const
      76             :     {
      77           0 :         return inSize;
      78             :     }
      79             : 
      80           0 :     virtual Status Reset()
      81             :     {
      82           0 :         m_checksum = InitializeChecksum();
      83           0 :         return INFO::OK;
      84             :     }
      85             : 
      86           0 :     virtual Status Process(const u8* in, size_t inSize, u8* out, size_t outSize, size_t& inConsumed, size_t& outProduced)
      87             :     {
      88           0 :         const size_t transferSize = std::min(inSize, outSize);
      89           0 :         memcpy(out, in, transferSize);
      90           0 :         inConsumed = outProduced = transferSize;
      91           0 :         m_checksum = UpdateChecksum(m_checksum, out, outProduced);
      92           0 :         return INFO::OK;
      93             :     }
      94             : 
      95           0 :     virtual Status Finish(u32& checksum, size_t& outProduced)
      96             :     {
      97           0 :         outProduced = 0;
      98           0 :         checksum = m_checksum;
      99           0 :         return INFO::OK;
     100             :     }
     101             : 
     102             : private:
     103             :     u32 m_checksum;
     104             : };
     105             : 
     106             : 
     107             : //-----------------------------------------------------------------------------
     108             : 
     109           0 : class CodecZLibStream : public Codec_ZLib
     110             : {
     111             : protected:
     112           0 :     CodecZLibStream()
     113           0 :     {
     114           0 :         memset(&m_zs, 0, sizeof(m_zs));
     115           0 :         m_checksum = InitializeChecksum();
     116           0 :     }
     117             : 
     118           0 :     static Status LibError_from_zlib(int zlib_ret)
     119             :     {
     120           0 :         switch(zlib_ret)
     121             :         {
     122           0 :         case Z_OK:
     123           0 :             return INFO::OK;
     124           0 :         case Z_STREAM_END:
     125           0 :             WARN_RETURN(ERR::FAIL);
     126           0 :         case Z_MEM_ERROR:
     127           0 :             WARN_RETURN(ERR::NO_MEM);
     128           0 :         case Z_DATA_ERROR:
     129           0 :             WARN_RETURN(ERR::CORRUPTED);
     130           0 :         case Z_STREAM_ERROR:
     131           0 :             WARN_RETURN(ERR::INVALID_PARAM);
     132           0 :         default:
     133           0 :             WARN_RETURN(ERR::FAIL);
     134             :         }
     135             :     }
     136             : 
     137           0 :     static void WarnIfZLibError(int zlib_ret)
     138             :     {
     139           0 :         (void)LibError_from_zlib(zlib_ret);
     140           0 :     }
     141             : 
     142             :     typedef int ZEXPORT (*ZLibFunc)(z_streamp strm, int flush);
     143             : 
     144           0 :     Status CallStreamFunc(ZLibFunc func, int flush, const u8* in, const size_t inSize, u8* out, const size_t outSize, size_t& inConsumed, size_t& outProduced)
     145             :     {
     146           0 :         m_zs.next_in  = (Byte*)in;
     147           0 :         m_zs.avail_in = (uInt)inSize;
     148           0 :         m_zs.next_out  = (Byte*)out;
     149           0 :         m_zs.avail_out = (uInt)outSize;
     150             : 
     151           0 :         int ret = func(&m_zs, flush);
     152             :         // sanity check: if ZLib reports end of stream, all input data
     153             :         // must have been consumed.
     154           0 :         if(ret == Z_STREAM_END)
     155             :         {
     156           0 :             ENSURE(m_zs.avail_in == 0);
     157           0 :             ret = Z_OK;
     158             :         }
     159             : 
     160           0 :         ENSURE(inSize >= m_zs.avail_in && outSize >= m_zs.avail_out);
     161           0 :         inConsumed  = inSize  - m_zs.avail_in;
     162           0 :         outProduced = outSize - m_zs.avail_out;
     163             : 
     164           0 :         return LibError_from_zlib(ret);
     165             :     }
     166             : 
     167             :     mutable z_stream m_zs;
     168             : 
     169             :     // note: z_stream does contain an 'adler' checksum field, but that's
     170             :     // not updated in streams lacking a gzip header, so we'll have to
     171             :     // calculate a checksum ourselves.
     172             :     // adler32 is somewhat weaker than CRC32, but a more important argument
     173             :     // is that we should use the latter for compatibility with Zip archives.
     174             :     mutable u32 m_checksum;
     175             : };
     176             : 
     177             : 
     178             : //-----------------------------------------------------------------------------
     179             : 
     180             : class Compressor_ZLib : public CodecZLibStream
     181             : {
     182             : public:
     183           0 :     Compressor_ZLib()
     184           0 :     {
     185             :         // note: with Z_BEST_COMPRESSION, 78% percent of
     186             :         // archive builder CPU time is spent in ZLib, even though
     187             :         // that is interleaved with IO; everything else is negligible.
     188             :         // we prefer faster speed at the cost of 1.5% larger archives.
     189           0 :         const int level      = Z_BEST_SPEED;
     190           0 :         const int windowBits = -MAX_WBITS;  // max window size; omit ZLib header
     191           0 :         const int memLevel   = 9;                   // max speed; total mem ~= 384KiB
     192           0 :         const int strategy   = Z_DEFAULT_STRATEGY;  // normal data - not RLE
     193           0 :         const int ret = deflateInit2(&m_zs, level, Z_DEFLATED, windowBits, memLevel, strategy);
     194           0 :         ENSURE(ret == Z_OK);
     195           0 :     }
     196             : 
     197           0 :     virtual ~Compressor_ZLib()
     198           0 :     {
     199           0 :         const int ret = deflateEnd(&m_zs);
     200           0 :         WarnIfZLibError(ret);
     201           0 :     }
     202             : 
     203           0 :     virtual size_t MaxOutputSize(size_t inSize) const
     204             :     {
     205           0 :         return (size_t)deflateBound(&m_zs, (uLong)inSize);
     206             :     }
     207             : 
     208           0 :     virtual Status Reset()
     209             :     {
     210           0 :         m_checksum = InitializeChecksum();
     211           0 :         const int ret = deflateReset(&m_zs);
     212           0 :         return LibError_from_zlib(ret);
     213             :     }
     214             : 
     215           0 :     virtual Status Process(const u8* in, size_t inSize, u8* out, size_t outSize, size_t& inConsumed, size_t& outProduced)
     216             :     {
     217           0 :         m_checksum = UpdateChecksum(m_checksum, in, inSize);
     218           0 :         return CodecZLibStream::CallStreamFunc(deflate, 0, in, inSize, out, outSize, inConsumed, outProduced);
     219             :     }
     220             : 
     221           0 :     virtual Status Finish(u32& checksum, size_t& outProduced)
     222             :     {
     223           0 :         const uInt availOut = m_zs.avail_out;
     224             : 
     225             :         // notify zlib that no more data is forthcoming and have it flush output.
     226             :         // our output buffer has enough space due to use of deflateBound;
     227             :         // therefore, deflate must return Z_STREAM_END.
     228           0 :         const int ret = deflate(&m_zs, Z_FINISH);
     229           0 :         ENSURE(ret == Z_STREAM_END);
     230             : 
     231           0 :         outProduced = size_t(availOut - m_zs.avail_out);
     232             : 
     233           0 :         checksum = m_checksum;
     234           0 :         return INFO::OK;
     235             :     }
     236             : };
     237             : 
     238             : 
     239             : //-----------------------------------------------------------------------------
     240             : 
     241             : class Decompressor_ZLib : public CodecZLibStream
     242             : {
     243             : public:
     244           0 :     Decompressor_ZLib()
     245           0 :     {
     246           0 :         const int windowBits = -MAX_WBITS;  // max window size; omit ZLib header
     247           0 :         const int ret = inflateInit2(&m_zs, windowBits);
     248           0 :         ENSURE(ret == Z_OK);
     249           0 :     }
     250             : 
     251           0 :     virtual ~Decompressor_ZLib()
     252           0 :     {
     253           0 :         const int ret = inflateEnd(&m_zs);
     254           0 :         WarnIfZLibError(ret);
     255           0 :     }
     256             : 
     257           0 :     virtual size_t MaxOutputSize(size_t inSize) const
     258             :     {
     259             :         // relying on an upper bound for the output is a really bad idea for
     260             :         // large files. archive formats store the uncompressed file sizes,
     261             :         // so callers should use that when allocating the output buffer.
     262           0 :         ENSURE(inSize < 1*MiB);
     263             : 
     264           0 :         return inSize*1032; // see http://www.zlib.org/zlib_tech.html
     265             :     }
     266             : 
     267           0 :     virtual Status Reset()
     268             :     {
     269           0 :         m_checksum = InitializeChecksum();
     270           0 :         const int ret = inflateReset(&m_zs);
     271           0 :         return LibError_from_zlib(ret);
     272             :     }
     273             : 
     274           0 :     virtual Status Process(const u8* in, size_t inSize, u8* out, size_t outSize, size_t& inConsumed, size_t& outProduced)
     275             :     {
     276           0 :         const Status ret = CodecZLibStream::CallStreamFunc(inflate, Z_SYNC_FLUSH, in, inSize, out, outSize, inConsumed, outProduced);
     277           0 :         m_checksum = UpdateChecksum(m_checksum, out, outProduced);
     278           0 :         return ret;
     279             :     }
     280             : 
     281           0 :     virtual Status Finish(u32& checksum, size_t& outProduced)
     282             :     {
     283             :         // no action needed - decompression always flushes immediately.
     284           0 :         outProduced = 0;
     285             : 
     286           0 :         checksum = m_checksum;
     287           0 :         return INFO::OK;
     288             :     }
     289             : };
     290             : 
     291             : 
     292             : //-----------------------------------------------------------------------------
     293             : 
     294           0 : PICodec CreateCodec_ZLibNone()
     295             : {
     296           0 :     return PICodec(new Codec_ZLibNone);
     297             : }
     298             : 
     299           0 : PICodec CreateCompressor_ZLibDeflate()
     300             : {
     301           0 :     return PICodec(new Compressor_ZLib);
     302             : }
     303             : 
     304           0 : PICodec CreateDecompressor_ZLibDeflate()
     305             : {
     306           0 :     return PICodec (new Decompressor_ZLib);
     307           3 : }

Generated by: LCOV version 1.13