LCOV - code coverage report
Current view: top level - libs/http_proto/src_zlib/service/zlib_service.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 80.4 % 153 123
Test Date: 2024-07-25 21:11:03 Functions: 73.9 % 23 17

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
       3              : //
       4              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6              : //
       7              : // Official repository: https://github.com/cppalliance/http_proto
       8              : //
       9              : 
      10              : #ifndef BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_IPP
      11              : #define BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_IPP
      12              : 
      13              : #include <boost/http_proto/service/zlib_service.hpp>
      14              : 
      15              : #include <boost/http_proto/metadata.hpp>
      16              : #include <boost/http_proto/detail/workspace.hpp>
      17              : 
      18              : #include <boost/assert/source_location.hpp>
      19              : #include <boost/buffers/circular_buffer.hpp>
      20              : #include <boost/config.hpp>
      21              : #include <boost/system/result.hpp>
      22              : #include <boost/throw_exception.hpp>
      23              : 
      24              : #include <zlib.h>
      25              : 
      26              : #include "../../src/zlib_service.hpp"
      27              : 
      28              : namespace boost {
      29              : namespace http_proto {
      30              : namespace zlib {
      31              : namespace detail {
      32              : 
      33              : /*
      34              :     DEFLATE Compressed Data Format Specification version 1.3
      35              :     https://www.rfc-editor.org/rfc/rfc1951
      36              : */
      37              : 
      38              : //------------------------------------------------
      39              : 
      40              : enum class error
      41              : {
      42              :     ok          =  0,
      43              :     stream_end  =  1,
      44              :     need_dict   =  2,
      45              :     errno_      = -1,
      46              :     stream_err  = -2,
      47              :     data_err    = -3,
      48              :     mem_err     = -4,
      49              :     buf_err     = -5,
      50              :     version_err = -6
      51              : };
      52              : 
      53              : //------------------------------------------------
      54              : } // detail
      55              : } // zlib
      56              : } // http_proto
      57              : namespace system {
      58              : template<>
      59              : struct is_error_code_enum<
      60              :     ::boost::http_proto::zlib::detail::error>
      61              : {
      62              :     static bool const value = true;
      63              : };
      64              : } // system
      65              : namespace http_proto {
      66              : namespace zlib {
      67              : namespace detail {
      68              : //------------------------------------------------
      69              : 
      70              : struct error_cat_type
      71              :     : system::error_category
      72              : {
      73              :     BOOST_SYSTEM_CONSTEXPR
      74            3 :     error_cat_type() noexcept
      75            3 :         : error_category(
      76            3 :             0xe6c6d0215d1d6e22)
      77              :     {
      78            3 :     }
      79              : 
      80              :     const char*
      81            0 :     name() const noexcept override
      82              :     {
      83            0 :         return "boost.http.proto.zlib";
      84              :     }
      85              : 
      86              :     std::string
      87            0 :     message( int ev ) const override
      88              :     {
      89            0 :         return message( ev, nullptr, 0 );
      90              :     }
      91              : 
      92              :     char const*
      93            0 :     message(
      94              :         int ev,
      95              :         char*,
      96              :         std::size_t) const noexcept override
      97              :     {
      98            0 :         switch(static_cast<error>(ev))
      99              :         {
     100            0 :         case error::ok: return "Z_OK";
     101            0 :         case error::stream_end: return "Z_STREAM_END";
     102            0 :         case error::need_dict: return "Z_NEED_DICT";
     103            0 :         case error::errno_: return "Z_ERRNO";
     104            0 :         case error::stream_err: return "Z_STREAM_ERROR";
     105            0 :         case error::data_err: return "Z_DATA_ERROR";
     106            0 :         case error::mem_err: return "Z_MEM_ERROR";
     107            0 :         case error::buf_err: return "Z_BUF_ERROR";
     108            0 :         case error::version_err: return "Z_VERSION_ERROR";
     109            0 :         default:
     110            0 :             return "unknown";
     111              :         }
     112              :     }
     113              : };
     114              : 
     115              : system::error_code
     116          104 : make_error_code(
     117              :     error ev) noexcept
     118              : {
     119              :     static BOOST_SYSTEM_CONSTEXPR
     120          104 :         error_cat_type cat{};
     121              :     return system::error_code{static_cast<
     122              :         std::underlying_type<
     123          104 :             error>::type>(ev), cat};
     124              : }
     125              : 
     126              : BOOST_NOINLINE BOOST_NORETURN
     127              : void
     128            0 : throw_zlib_error(
     129              :     int e,
     130              :     source_location const& loc = BOOST_CURRENT_LOCATION)
     131              : {
     132            0 :     throw_exception(
     133            0 :         system::system_error(static_cast<error>(e)), loc);
     134              : }
     135              : 
     136              : //------------------------------------------------
     137              : 
     138              : // probes memory usage for a config
     139              : class probe
     140              : {
     141              : public:
     142              :     explicit
     143           26 :     probe() noexcept
     144           26 :     {
     145           26 :         zs_.zalloc = &zalloc;
     146           26 :         zs_.zfree = &zfree;
     147           26 :         zs_.opaque = this;
     148           26 :     }
     149              : 
     150              :     system::result<std::size_t>
     151           26 :     deflate_init(
     152              :         int level)
     153              :     {
     154           26 :         n_ = 0;
     155           26 :         system::error_code ec;
     156              :         ec = static_cast<error>(
     157           26 :             deflateInit(&zs_, level));
     158           26 :         if(ec.failed())
     159            0 :             return ec;
     160           26 :         Bytef tmp[24]{};
     161           26 :         zs_.next_in = &tmp[0];
     162           26 :         zs_.avail_in = 1;
     163           26 :         zs_.next_out = &tmp[1];
     164           26 :         zs_.avail_out = 23;
     165              :         ec = static_cast<error>(
     166           26 :             deflate(&zs_,
     167           26 :                 Z_FINISH));
     168           52 :         if( ec.failed() &&
     169           52 :             ec != error::stream_end)
     170            0 :             return ec;
     171              :         ec = static_cast<error>(
     172           26 :             deflateEnd(&zs_));
     173           26 :         if(ec.failed())
     174            0 :             return ec;
     175           26 :         return n_;
     176              :     }
     177              : 
     178              :     system::result<std::size_t>
     179              :     deflate_init2(
     180              :         int level,
     181              :         int method,
     182              :         int windowBits,
     183              :         int memLevel,
     184              :         int strategy)
     185              :     {
     186              :         n_ = 0;
     187              :         system::error_code ec;
     188              :         ec = static_cast<error>(
     189              :             deflateInit2(&zs_,
     190              :                 level,
     191              :                 method,
     192              :                 windowBits,
     193              :                 memLevel,
     194              :                 strategy));
     195              :         if(ec.failed())
     196              :             return ec;
     197              :         Bytef tmp[2];
     198              :         zs_.next_in = &tmp[0];
     199              :         zs_.avail_in = 0;
     200              :         zs_.next_out = &tmp[1];
     201              :         zs_.avail_out = 0;
     202              :         ec = static_cast<error>(
     203              :             deflate(&zs_,
     204              :                 Z_FULL_FLUSH));
     205              :         if(ec.failed())
     206              :             return ec;
     207              :         ec = static_cast<error>(
     208              :             deflateEnd(&zs_));
     209              :         if(ec.failed())
     210              :             return ec;
     211              :         return n_;
     212              :     }
     213              : 
     214              : private:
     215          130 :     static void* zalloc(void* opaque,
     216              :         uInt num, uInt size)
     217              :     {
     218          130 :         auto& self =
     219              :             *reinterpret_cast<
     220              :                 probe*>(opaque);
     221          130 :         self.n_ += num * size;
     222          130 :         return new char[num * size];
     223              :     }
     224              : 
     225          130 :     static void zfree(
     226              :         void*, void* address)
     227              :     {
     228          130 :         delete[] reinterpret_cast<
     229          130 :             char*>(address);
     230          130 :     }
     231              : 
     232              :     z_stream_s zs_{};
     233              :     std::size_t n_ = 0;
     234              : };
     235              : 
     236              : //------------------------------------------------
     237              : 
     238              : 
     239              : namespace {
     240          240 : void* zalloc_impl(
     241              :     void* opaque,
     242              :     unsigned items,
     243              :     unsigned size)
     244              : {
     245              :     try
     246              :     {
     247          240 :         auto n = items * size;
     248          240 :         auto* ws =
     249              :             reinterpret_cast<
     250              :                 http_proto::detail::workspace*>(opaque);
     251              : 
     252          240 :         return ws->reserve_front(n);
     253              :     }
     254            0 :     catch(std::length_error const&) // represents OOM
     255              :     {
     256            0 :         return Z_NULL;
     257            0 :     }
     258              : }
     259              : 
     260          240 : void zfree_impl(void* /* opaque */, void* /* addr */)
     261              : {
     262              :     // we call ws_.clear() before the serializer is reused
     263              :     // so all the allocations are passively freed
     264          240 : }
     265              : 
     266              : } // namespace
     267              : 
     268              : class BOOST_HTTP_PROTO_ZLIB_DECL
     269              :     deflate_filter final : public filter
     270              : {
     271              : private:
     272              :     z_stream stream_;
     273              :     http_proto::detail::workspace& ws_;
     274              : 
     275              :     void init(bool use_gzip);
     276              : 
     277              : public:
     278              :     deflate_filter(
     279              :         http_proto::detail::workspace& ws,
     280              :         bool use_gzip = false);
     281              :     ~deflate_filter();
     282              : 
     283              :     deflate_filter(deflate_filter const&) = delete;
     284              :     deflate_filter& operator=(
     285              :         deflate_filter const&) = delete;
     286              : 
     287              :     filter::results
     288              :     on_process(
     289              :         buffers::mutable_buffer out,
     290              :         buffers::const_buffer in,
     291              :         bool more) override;
     292              : };
     293              : 
     294           48 : deflate_filter::
     295              : deflate_filter(
     296              :     http_proto::detail::workspace& ws,
     297           48 :     bool use_gzip)
     298           48 :     : ws_(ws)
     299              : {
     300           48 :     stream_.zalloc = &zalloc_impl;
     301           48 :     stream_.zfree = &zfree_impl;
     302           48 :     stream_.opaque = &ws_;
     303           48 :     init(use_gzip);
     304           48 : }
     305              : 
     306           48 : deflate_filter::
     307           48 : ~deflate_filter()
     308              : {
     309           48 :     deflateEnd(&stream_);
     310           48 : }
     311              : 
     312              : void
     313           48 : deflate_filter::
     314              : init(bool use_gzip)
     315              : {
     316           48 :     int ret = -1;
     317              : 
     318           48 :     int window_bits = 15;
     319           48 :     if( use_gzip )
     320           24 :         window_bits += 16;
     321              : 
     322           48 :     int mem_level = 8;
     323              : 
     324           48 :     ret = deflateInit2(
     325              :         &stream_, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
     326              :         window_bits, mem_level, Z_DEFAULT_STRATEGY);
     327              : 
     328           48 :     if( ret != Z_OK )
     329            0 :         throw_zlib_error(ret);
     330              : 
     331           48 :     stream_.next_out = nullptr;
     332           48 :     stream_.avail_out = 0;
     333              : 
     334           48 :     stream_.next_in = nullptr;
     335           48 :     stream_.avail_in = 0;
     336           48 : }
     337              : 
     338              : filter::results
     339        23724 : deflate_filter::
     340              : on_process(
     341              :     buffers::mutable_buffer out,
     342              :     buffers::const_buffer in,
     343              :     bool more)
     344              : {
     345        23724 :     auto& zstream = stream_;
     346              : 
     347        23724 :     auto flush = more ? Z_NO_FLUSH : Z_FINISH;
     348        23724 :     int ret = -1;
     349        23724 :     filter::results results;
     350              : 
     351              :     for(;;)
     352              :     {
     353        45988 :         zstream.next_in =
     354              :             reinterpret_cast<unsigned char*>(
     355        45988 :                 const_cast<void*>(in.data()));
     356        45988 :         zstream.avail_in = static_cast<unsigned>(
     357        45988 :             in.size());
     358              : 
     359        45988 :         zstream.next_out =
     360              :             reinterpret_cast<unsigned char*>(
     361        45988 :                 out.data());
     362        45988 :         zstream.avail_out =
     363        45988 :             static_cast<unsigned>(out.size());
     364              : 
     365        45988 :         auto n1 = zstream.avail_in;
     366        45988 :         auto n2 = zstream.avail_out;
     367        45988 :         ret = deflate(&zstream, flush);
     368              : 
     369        45988 :         in += (n1 - zstream.avail_in);
     370        45988 :         out += (n2 - zstream.avail_out);
     371              : 
     372        45988 :         results.in_bytes += (n1 - zstream.avail_in);
     373        45988 :         results.out_bytes += (n2 - zstream.avail_out);
     374              : 
     375        45988 :         auto is_empty = (in.size() == 0);
     376              : 
     377        45988 :         if( ret != Z_OK &&
     378           96 :             ret != Z_BUF_ERROR &&
     379              :             ret != Z_STREAM_END )
     380            0 :             throw_zlib_error(ret);
     381              : 
     382        45988 :         if( is_empty &&
     383        44648 :             n2 == zstream.avail_out &&
     384              :             ret == Z_OK )
     385              :         {
     386        11104 :             flush = Z_SYNC_FLUSH;
     387        11104 :             continue;
     388              :         }
     389              : 
     390        34884 :         if( ret == Z_STREAM_END )
     391           96 :             results.finished = true;
     392              : 
     393        34884 :         if( ret == Z_BUF_ERROR )
     394        22104 :             break;
     395              : 
     396        12780 :         if( ret == Z_STREAM_END )
     397           96 :             break;
     398              : 
     399        25368 :         if( ret == Z_OK &&
     400        12684 :             out.size() == 0 )
     401         1524 :             break;
     402        22264 :     }
     403        23724 :     return results;
     404              : }
     405              : 
     406              : //------------------------------------------------
     407              : 
     408              : struct
     409              :     deflate_decoder_service_impl
     410              :     : deflate_decoder_service
     411              : {
     412              :     using key_type =
     413              :         deflate_decoder_service;
     414              : 
     415              :     explicit
     416           26 :     deflate_decoder_service_impl(
     417              :         context& ctx,
     418              :         config const& cfg)
     419           26 :         : cfg_(cfg)
     420              :     {
     421              :         (void)ctx;
     422           26 :         probe p;
     423           26 :         auto n0 = p.deflate_init(
     424           26 :             Z_DEFAULT_COMPRESSION).value();
     425              :         (void)n0;
     426           26 :     }
     427              : 
     428              : private:
     429              :     config cfg_;
     430              : 
     431              :     config const&
     432            0 :     get_config() const noexcept override
     433              :     {
     434            0 :         return cfg_;
     435              :     }
     436              : 
     437              :     std::size_t
     438            1 :     space_needed() const noexcept override
     439              :     {
     440            1 :         return 0;
     441              :     }
     442              : 
     443              :     filter&
     444           24 :     make_deflate_filter(
     445              :         http_proto::detail::workspace& ws) const override
     446              :     {
     447           24 :         return ws.emplace<deflate_filter>(ws, false);
     448              :     }
     449              : 
     450              :     filter&
     451           24 :     make_gzip_filter(
     452              :         http_proto::detail::workspace& ws) const override
     453              :     {
     454           24 :         return ws.emplace<deflate_filter>(ws, true);
     455              :     }
     456              : };
     457              : 
     458              : } // detail
     459              : 
     460              : void BOOST_HTTP_PROTO_ZLIB_DECL
     461           26 : install_deflate_encoder(context& ctx)
     462              : {
     463           26 :     detail::deflate_decoder_service::config cfg;
     464              :     ctx.make_service<
     465           26 :         detail::deflate_decoder_service_impl>(cfg);
     466           26 : }
     467              : 
     468              : } // zlib
     469              : } // http_proto
     470              : } // boost
     471              : 
     472              : #endif
        

Generated by: LCOV version 2.1